标签:
类只存在一个实例,即只可以创建一个对象。有一些类如果创造出多个对象就会导致许多问题的产生,如程序的行为异常、资源使用过量,或者是不一致的结果。单件模式常常被用来管理共享的资源,例如数据库连接或者线程池。
简要定义:单件模式确保一个类只有一个实例,并提供一个全局访问点
单件模式的实现是通过private构造函数,类中含有一个静态方法getInstance(),调用这个方法可以创建并返回唯一的对象,也可能返回的是已经创建好的对象。
单件模式,首先要限制从外部创建对象,所以将构造方法声明为私有private,这样就只能在类内部调用构造函数。
如何调用这个构造函数?要使用这个类,而又没有这个类的对象,所以需要一个静态的方法,这个方法调用私有的构造函数,并且返回唯一的单件对象。
剖析经典单件模式的实现
下面是经典单件模式的实现:
public class Singleton { private static Singleton singleton = null; private Singleton() { } public static Singleton getInstance() { if(singleton== null) { singleton=newSingleton(); } return singleton; } }这种实现方式存在问题,在多线程的情况下,可能会创建多个对象。
我们可以通过增加synchionized关键字到getInstance方法中,迫使每个线程在进入这个方法之前,要先等候别的进程离开该方法。
public class Singleton { private static Singleton singleton = null; private Singleton() { } public static synchionized Singleton getInstance() { if(singleton== null) { singleton=newSingleton(); } return singleton; }这样的同步可能会造成性能问题,改善途径有两种:
1.使用“急切”创建实例,而不用延迟实例化的做法:
public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return uniqueInstance; } }利用这个做法,我们依赖JVM在加载在加载这个类时马上创建唯一的单件实例.JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例
如果我们希望他能在我第一次getInstance()时才被真正的创建。这样,我们可以控制真正的类创建的时刻,而不是把类的创建委托给了类装载器。可以用如下版本实现:
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = newSingleton(); } privateSingleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }上面这种方式,仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它只有在getInstance()被调用时才会真正创建;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
2.使用"双重检查加锁“,在getInstance中减少使用同步:
首先检查是否实例已经创建了,如果未创建,才进行同步,这样一来,只有第一次会同步。
public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() {} public static Singleton getInstance() { if (uniqueInstance == null) { synchronized (Singleton.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
= new Singleton()
这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。
volatile 关键字的作用就是确保当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量
标签:
原文地址:http://blog.csdn.net/u010786109/article/details/45937737