设计模式之单例模式

来源:互联网 发布:质量好的挎包淘宝店 编辑:程序博客网 时间:2024/05/22 03:49

单例模式

单例模式:确保一个类只有一个实例,并提供一个全局访问点。
单例模式用来创建独一无二的,只能有一个实例的对象的入场券。
问:这样做有什么好处呢?
答:有一些对象其实我们只需要一个,比方说:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置和注册表(registry)的对象、日志对象,充当打印机、显卡等设备的驱动程序对象。事实上,这类对象只能有一个实例,如果制造出多个实例,就会导致许多问题产生,例如:程序的行为异常、资源使用过量,或者是不一致的结果。
经典的单例模式实现:
package com.my.singleton;public class Singleton {    //利用一个静态变量来记录Singleton类的唯一实例。    private static Singleton uniqueInstance;    //把构造器声明为私有的,只有从Singleton类内才可以调用构造器。    private Singleton(){}    public static Singleton getInstance(){//用getInstance()方法实例化对象,                                            // 并返回这个实例。        if(uniqueInstance==null){            //如果这个类不存在,我们就用私有的构造器产生一个Singleton实例并            // 把它赋值给uniqueInstance            uniqueInstance = new Singleton();        }            return uniqueInstance;    }}
单例模式类图:

现在我们的单例代码还有一个地方没有考虑到,那就是对于多线程的处理。多个线程执行时就无法保证对象的唯一性,如果同一时间有多个线程进入if语句当中,就有可能产生多个对象。这个时候我们只需要把getInstance()方法变成同步(synchronized)方法,多线程就可以轻易的解决了。
package com.my.singleton;public class Singleton {    private static Singleton uniqueInstance;    private Singleton(){}    //通过增加synchroinzed关键字到getInstance()方法中,我们迫使每个线程在进入这个方法之前,要先等候别的线程离开该方法。
   //也就是说,不会有两个线程可以同时进入这个方法。    public static synchronized Singleton getInstance(){        if(uniqueInstance==null){            uniqueInstance = new Singleton();        }            return uniqueInstance;    }}
现在同步的问题解决了,但同时又会产生另一个问题:同步会导致性能降低。只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步是一种累赘。
解决办法:
1.使用“急切”的方式来创建实例,而不用延迟实例化的做法
package com.my.singleton;public class Singleton {    //在静态初始化器(static initializen)中创建单例。这段代码保证了线程的安全。    private static Singleton uniqueInstance = new Singleton();    private Singleton(){}    public static synchronized Singleton getInstance(){            return uniqueInstance;    }}
我们依赖JVM在加载这个类时马上创建此唯一的单例。JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例。
2.用“双重检查加锁”,在getInstance()中减少使用同步
package com.my.singleton;public class Singleton {    //volatile关键词确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程可以    //正确的处理uniqueInstance变量。    private volatile static Singleton uniqueInstance;    private Singleton(){}    public static synchronized Singleton getInstance(){        if(uniqueInstance==null){//检查实例,如果不存在就进入同步区块            //注意只有第一次才彻底执行这里的代码。            synchronized(Singleton.class){                if(uniqueInstance==null){                    //进入区块再检查一次。如果仍是null,才创建实例。                    uniqueInstance = new Singleton();                }            }        }            return uniqueInstance;    }}

这样就解决了同步导致性能降低的问题。
最后有一点需要注意:双重加锁不适用于1.4及更早版本的java。


原创粉丝点击