当我们希望某个类在应用程序执行过程中有且仅有一个实例的时候,就可以用到单例模式了。
双重检查加锁方式:
// final类不被继承
public final class DCLSingleton {
private static volatile DCLSingleton instance;
//单例中我们要获取的资源
public Resource resource = null;
private DCLSingleton() {
//...这里进行resource的创建
resource = ... ;
}
/**
* 双重校验,当instance不为null时,获得当前类的锁,然后再次判断instance是否等于null:
* 这是为防止在多个线程调用情况下,当线程A判断instance为null,获得类锁后,创建新的实例,
* 此时如果新的实例还未创建完毕时,线程B判断instanc为null,在进行类锁获取时,线程A刚好创建完实例释放类锁,并由线程B获得,
* 那么如果没有第二个等于null的判断的话,则又会创建另一个新的实例。所以这种单例模式就称为双重检查加锁模式。
* @return
*/
public static DCLSingleton getInstance() {
if(instance == null) {
synchronized(DCLSingleton.class) {
if(instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
上面的方式看起来做了很充分的检查,但在多线程情况仍有可能出现空指针异常:
我们在私有构造器中进行资源实例化,还有单例自身,根据JVM运行时指令重排序和Happens-Before规则,这两者的实例化顺序并无前后关系的约束,有可能单例实例最先被实例化,而资源尚未完成实例化,此时其他线程此时调用getInstance()方法获得单例实例,如果直接调用resource方法则会抛出空指针异常。
以下是改进后的单例实现:
Holder方式:
public final class HolderSingleton {
// 私有构造器
private HolderSingleton() {
// ...资源实例创建
}
// 私有静态类
private static class Holder {
private static HolderSingleton instance = new HolderSingleton();
}
//通过内部静态类来创建实例
public static HolderSingleton getInstance() {
return Holder.instance;
}
//单例中的资源
private static Resource resource = null;
public static Resource getResource() {
return resource;
}
}
instance被放置到静态内部类Holder中,HolderSingleton类的初始化不会创建实例,当Holder被主动引用时才创建。
网友评论