前面说过的单例模式为了避免被不同的线程创建多次,各个线程引用了不同的实例,这违背了我们的“唯一实例”的初衷,因此,我们采取了同步的机制(
synchronized
),代码如下:
public class Singleton { private static Singleton single = null; private Singleton() { System.out.println("create a singleton class."); } public synchronized static Singleton getInstance() { if(null == single) { single = new Singleton(); } return single; } public void dosth() { System.out.println("do somethings."); } } public static void main(String[] args) { // TODO Auto-generated method stub Singleton single = Singleton.getInstance(); single.dosth(); }
但是这样带来了一个新的问题:性能问题。每次访问getInstance都需要进行同步。事实上,我们只需在构造第一个也是唯一一个Singleton类实例的时候进行同步。
我们采用一个Double Check的方式修正上面的代码。
public static Singleton getInstance(){ if(singleton == null){ synchronized(Singleton.class){ if(singleton == null) singleton = new Singleton(); } } return singleton; }
Double Check方式改进:
- 虽然是多线程环境,但如果没有外层if,客户程序每次执行时都要先synchronized为Singleton类型,但是在绝大多数情况下,每次锁定synchronized类型效率太低,这个锁定很可能就成了整个应用的瓶颈。
- synchronized加内层的if 组成了一个相对线程安全的实例构造环境。
虽然看起来Double Check方式很完美,但事实上这个方案还是存在问题:多线程环境下不能确保singleton == null操作的原子性。
修改后的Double Check方式代码:
public class Singleton { private static volatile Singleton singleton = null; private Singleton() { } public static Singleton getInstance() { if(singleton == null) { synchronized(Singleton.class) { if(singleton == null) { singleton = new Singleton(); } } } return singleton; } }