设计模式之单例模式

来源:互联网 发布:淘宝店铺网名昵称大全 编辑:程序博客网 时间:2024/06/05 22:39

    单例模式可以说是设计模式中最简单的模式,但是呢用处以及掌握起来还是需要一定的时间和精力的,下面介绍单利模式;

   有一些对象我们只需要有一个就够了,比方说:线程池,缓存,对话框,处理偏好设置和注册表等的对象,日志对象,打印机,显卡设备的驱动,事实上,这些都只能有一个实例对象,如果有多个会产生很多问题。

       参考下面的代码:

public class Singleton {private static Singleton uniqueInstance;//其它的实例化变量private Singleton(){}public static Singleton getInstance(){if(uniqueInstance==null){uniqueInstance = new Singleton();}return uniqueInstance;}}
这样呢就创建了一个单例类,当利用私有的构造器产生一个Singleton实例并把它赋值到uniqueInstance中去,如果我们不需要这个实例,这个实例永远不会产生,这就是延迟实例化,假如uniqueInstance不是null,那就表示之前已经创建过对象,就可以直接用return语句返回。

然而我们给出的单件模式的定义是:确保一个类只有一个实例,并且只提供一个全局访问点。通过上面的设计我们可以看出,这个类只能借助私有的构造器构造这个类,而且也只能在类的内部完成实例化的过程,对于外部只提供了一个getInstance()方法来使得这个类得以创建。这样也可以做到在哪里使用这个类,就在这里创建。

然而当遇到多个线程共同去调用getInstance()方法,如上面的类,则会出现错误了,在多个线程之中,每次都会去调用方法创建,然后都会执行判断,假设当一个线程正在创建上述类的实例,而此时另一个线程以来访问时,还没有完成创建,条件还是会执行,就会创造出另一个上述类的实例对象,这样就产生了两个单利模式的对象,这样就违背了单例模式的设计原则,从而出现了一个严重的问题,怎么解决呢,我们首先想到的时sychornized关键字,代码如下:

public static synchronized Singleton getInstance(){if(uniqueInstance==null){uniqueInstance = new Singleton();}return uniqueInstance;}}
这样限制确实是可以解决问题,不会有两个线程同时进入方法,但是呢,我们会发现只有再第一次调用的时候才会真的需要同步关键字来限制,然而一旦设置好uniqueInstance变量后这个方法就不需要同步了,这样的化每次调用都是一次累赘,多余的开销。有更好一点的办法吗?看下面分析:

1.如果getInstance()的性能对应用程序不是很关键,就什么不用做了,如果程序能够接受由这个方法给程序造成的影像,那就使用同步getInstance()方法,这样的方法及简单有有效,但是,同步一个方法可能导致效率下降100倍,在频繁使用这个方法的地方就得重新考虑了。

2.我们可以直接创建实例,而不去延迟实例化,假如在程序的创建和运行时发面负担不重的化,可以考虑这么做:

public class Singleton {private static Singleton uniqueInstance = new Singleton();//其它的实例化变量private Singleton(){}public static synchronized Singleton getInstance(){return uniqueInstance;}}
这样可以保证再JVM中保证任何线程访问uniqueInstance变量之前首先创建了Singleton的实例。
3.使用双重检查锁机制再getInstance()中减少使用同步,

public class Singleton {private volatile static Singleton uniqueInstance = new Singleton();//其它的实例化变量private Singleton(){}public static Singleton getInstance(){if(uniqueInstance==null){ synchronized(Singleton.class){ uniqueInstance = new Singleton(); }}return uniqueInstance;}}

使用volatile关键字确保当uniqueInstance变量初始化城Singleton实例时,多个线程正确的处理uniqueInstance变量,这样就保证了只有第一次访问的时候创建实例,不存在的时候就进入同步区。这样可以减少getInstance()方法耗费的时间。

    对于垃圾回收器会吃掉单件,这其实是过分的夸大了,在Java1.2之前,垃圾回收器有个bug会造成单件在没有全局的引用时被当作垃圾进行清除,也就是说如果一个单体只有笨单件类引用它本身,那么就会被当作垃圾进行清除,,下次就会产生一个新的单件,对于多线程来说这是一种灾难。

在Java1.2之后修正了这个bug也不再需要一个全局引用来保护单件,如果出于某些原因使用旧版本的化,则需要注意这个问题。









原创粉丝点击