单例模式

Posted 酷叮喵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式相关的知识,希望对你有一定的参考价值。

1、构造函数弄成private 就是单例模式,即不想让别人用new 方法来创建多个对象,可以在类里面先生成一个对象,然后写一个public static方法把这个对象return出去。(eg:public 类名 getInstancd()return 你刚刚生成的那个类对象;),用static是因为你的构造函数是私有的,不能产生对象,所以只能用类名调用,所有只能是静态函数。成员变量也可以写getter/setter供外界访问的。
2、单例模式有一下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。
3、在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。
一、懒汉式(加synchronized则线程安全)

public class Singleton 
    private static Singleton instance;
    private Singleton ()

    public static [synchronized] Singleton getInstance() 
    if (instance == null) 
        instance = new Singleton();
    
    return instance;
    

但是效率很低,而且99%情况下不需要同步。
二、饿汉式(基于classloder机制避免了多线程的同步问题)

public class Singleton 
    private static Singleton instance = new Singleton();
    private Singleton ()
    public static Singleton getInstance() 
    return instance;
    

但是,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
三、静态内部类(只有调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance)

public class Singleton 
    private static class SingletonHolder 
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton ()
    public static final Singleton getInstance() 
    return SingletonHolder.INSTANCE;
    

同样利用了classloder的机制来保证初始化instance时只有一个线程,差别:饿汉式只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。
想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。静态内部类用于此场景
四、双重检验锁(实现线程安全,且性能消耗不大)

public class Singleton 
    private volatile static Singleton singleton;
    private Singleton ()
    public static Singleton getSingleton() 
    //先检查实例是否存在,如果不存在才进入下面的同步块
  if(instance==null)
   //同步块,线程安全的创建实例
   synchronized(Singleton_volatile.class)
    //再次检查实例是否存在,如果不存在才真正的创建实例
    if(instance==null)
     instance=new Singleton_volatile();
    
   
  
    return singleton;
    

并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的 情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
* 双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

  • 说明:由于volatile关键字可能会屏蔽掉虚拟机中的一些必要的代码优化,所以运行效率并不是 很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用”双重检查加锁“机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用。

参考:http://cantellow.iteye.com/blog/838473
http://blog.sina.com.cn/s/blog_6d2890600101gb8p.html
逻辑的话,下面这个比较好
http://devbean.blog.51cto.com/448512/203501/

以上是关于单例模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式-单例模式JAVA实现

设计模式-单例模式JAVA实现

单例模式

C++中的单例模式

设计模式-单例模式-指令重排思考

单例模式的简单理解