zoukankan      html  css  js  c++  java
  • Java设计模式之单例

    一、Java中的单例:

    特点:

    ① 单例类只有一个实例

    ② 单例类必须自己创建自己唯一实例

    ③ 单例类必须给所有其他对象提供这一实例

    二、两种模式:

    ①懒汉式单例<线程不安全>

    在类加载时,不创建实例,运行调用时创建。类加载快,在运行时获取对象速度慢

    示例:

    //懒汉模式
    public class Pet {
        
        private Pet(){
            
        }
        
        private static Pet pet=null;
        
        public static Pet getInfo(){
            if(pet==null){
                pet=new Pet();
            }
            return pet;
        }
    
    }

    ②饿汉式单例<线程安全>

    在类加载的时候,就完成初始化。所以类加载慢,但是在运行时获取对象快

    示例:

    //饿汉模式
    public class Pet {
        
        private Pet(){
            
        }
        
        private static Pet pet=new Pet();
        
        public static Pet getInfo(){
            return pet;
        }
    
    }

    饿汉模式线程安全,但是,懒汉模式线程安全性不高,若是多线程,又怎能保证单例?

    第一种方法:同步

          在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的

     public static synchronized Pet getInfo(){
            if(pet==null){
                pet=new Pet();
            }
            return pet;
        }

    第二种方法:双重检查锁定

          在getInfo中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

    所谓“双重检查加锁”机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

      “双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

     public static Pet getInfo(){
            if(pet==null){
                synchronized(Pet.class){
                     if(pet==null){
                         pet=new Pet();
                      }
                 }
            }
            return pet;
        }

    第三种方法:静态内部类

           利用了classloader的机制来保证初始化时只有一个线程,所以也是线程安全的,同时没有性能损耗

    public class Pet {    
        private static class LazyHolder {    
           private static final Pet INSTANCE = new Pet();    
        }    
        private Pet (){}    
        public static final Singleton getInfo() {    
           return LazyHolder.INSTANCE;    
        }    
    }  

    资源加载和性能:

          饿汉模式在类创建的同时就实例化一个静态对象,不管之后会不会使用这个单例,都会占用一定的内存,但是相应的,在第一次调用时速度也会非常快,因为其资源已经初始化完成。

          懒汉模式会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉模式一样可。

  • 相关阅读:
    Intellij Idea 设置之方法快速显示
    HTML转码码
    MIT自然语言处理第五讲:最大熵和对数线性模型(第一部分)
    MIT自然语言处理第五讲:最大熵和对数线性模型(第二部分)
    MIT自然语言处理第五讲:最大熵和对数线性模型(第四部分)
    MIT自然语言处理第五讲:最大熵和对数线性模型(第三部分)
    文本分类专题(ultimate 版)绝对是目前最全的C++版开源文本分类代码和最令人耳目一新的实验解释
    intellij idea教程
    [转] 一个大数相乘的C/C++实现
    5个海盗分金币的问题
  • 原文地址:https://www.cnblogs.com/jingpeipei/p/5534671.html
Copyright © 2011-2022 走看看