单例模式(Singleton Pattern)用于确保系统中某个类只有一个实例存在,即该类被创建初始化一次后,之后都不会再被创建。这里就来看下单例模式在Java中常见的几种实现方式:
双重检查锁
先来看下单例模式的简单实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public class SimpleSingletonUnsafe { private static SimpleSingletonUnsafe sInstance = null;
private SimpleSingletonUnsafe(){}
public static SimpleSingletonUnsafe getInstance(){ if(sInstance == null){ sInstance = new SimpleSingletonUnsafe(); } return sInstance; } }
|
由于没有对代码进行同步保护,上述实现在多个线程并发访问时可能出现sInstance被初始化多次的情况。为避免这种情况,可以直接在声明静态变量时进行类的初始化,从而保证始终只有一次初始化动作(JVM在初始化类时会获取一个锁,确保类初始化是线程安全的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class SimpleSingletonSafe { private static SimpleSingletonSafe INSTANCE = new SimpleSingletonSafe();
private SimpleSingletonSafe(){}
public static SimpleSingletonSafe getInstance(){ return INSTANCE; }
}
|
现在有了一个线程安全的单例模式了(Eager Initialization),但是这里还存在一个问题: 不管是否有线程使用该类,它都会初始化一次。这对于那些占有很多资源的类来说,可能并不合适。能不能只是在需要使用的时候才会类进行初始化了?采用双重检查锁(double checking lock)技术即可避免这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public class LazySingletonUnsafe { private static LazySingletonUnsafe sInstance = null; private LazySingletonUnsafe(){} public static LazySingletonUnsafe getInstance(){ if(sInstance == null){ synchronized(LazySingletonUnsafe.class){ if(sInstance == null){ sInstance = new LazySingletonUnsafe(); } } } return sInstance; }
}
|
表面看起来,这个方案似乎已经大功告成了。但在多线程的情况下,可能返回一个未初始化完成的对象。比如,有两个线程A、B,线程A先获取到同步锁,进入初始化代码,此时类LazySingletonUnsafe开始初始化,此时sInstance已经指向类分配的内存空间,不为空,当线程B调用getInstance时,判断条件不成立,因此直接准备返回,而实际上返回的是sInstance的一个引用对象,而可能此时类的初始化并没有完成,B得到的类实例就与A得到的不一致了。通过在限定变量sInstance为volatile(禁止JVM进行指令重排序,确保引用sInstance
在多线程情况下是可见的),即可解决该问题:
| 有关volatile
在JVM中的实现原理可参考:Java并发编程之Java Memory Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public class LazySingletonSafe { private volatile static LazySingletonSafe sInstance = null;
private LazySingletonSafe(){}
public static LazySingletonSafe getInstance(){ if(sInstance == null){ synchronized(LazySingletonUnsafe.class){ if(sInstance == null){ sInstance = new LazySingletonUnsafe(); } } } return sInstance; } }
|
Java类初始化的详细过程: http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2
类初始化
与此前双重检查锁中方案2类似,通过一个静态的私有类,类的对象变量作为该私有类的静态成员,该静态成员变量初始化时即创建新的对象,这样即能确保线程安全,也能保证类始终只初始化一次:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class SingletonHolder { private static class InstanceHolder{ public static final SingletonHolder INSTANCE = new SingletonHolder(); }
public static SingletonHolder getInstance(){ return InstanceHolder.INSTANCE; }
}
|
enum类型实现
在《Effective Java 2nd edition》第二章里推荐一种利用enum来实现单例模式的方法,看起来似乎更为简单:
1 2 3 4 5 6 7 8 9 10
| public enum EnumSingleton { INSTANCE;
@Override public String toString(){ return "EnumSingleton"; } }
|
另外,其中还提到了单例模式下类序列化的问题:为了避免每次序列化时都创建一个新的对象,需要提供一个readResolve
函数:
1 2 3 4 5
| private void readResolve(){ return INSTANCE; }
|
上述示例代码可参考:https://github.com/runningforlife/JavaExamples