最近在看设计模式,之前也断断续续了解了一些,但理解都不是很深,所以想系统的学习一下。
刚开始就从简单的入手,很熟悉的单例模式:
自己之前曾经这样用过:据说叫饿汉式,我想了半天不知饿汉是什么意思。Wikipedia给了一个名词叫Traditional simple way.我喜欢这个名字,简单好记,很形象。

public class Singleton{
private static final Singleton aInstance = new Singleton();
public static Singleton getInstance(){
return aInstance;
}
}


当然,为了防止调用者私自new出Singleton,可以再加一个方法:

private Singleton(){}

JVM只会对静态初始化初始一次,所以这样写解决了单例的要求。由于这种写法在类加载的时候会立即对对象初始化,于是又有另一种推迟初始化的写法(懒汉式 Lazy initialization):

public class Singleton{
private static Singleton aInstance = null;
public static Singleton getInstance(){
if(aInstance == null){
aInstance = new Singleton();
}
return aInstance;
}
}

这样加载固然没有问题,但当对多线程的时候问题就来了。这个方法不是线程安全的。于是为了解决这个问题,我的第一个想法是加上synchronized关键字。没错ok,是一个解法,但当参考资料的时候,结果发现有大牛这样解决:

public class Singleton{
private volatile static Singleton aInstance = null;
public static Singleton getInstance(){
if(aInstance == null){
synchronized(this){
if(aInstance == null){
aInstance == new Singleton();
}
}
}
return aInstance;
}
}

这样解决了高并发量时的 synchronized 关键字降低效率的问题(synchronized 修饰的部分只会在第一次初始化的时候执行)
使用 volatile 关键字是因为它具有 synchronized 可见的特点,线程会实时得知aInstance是否实例成功。保证了线程安全,被叫做Double-checked locking.
但有人提示说volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高。没有特殊需要的情况下,这种方法也不一定能够提高效率。
除了这种解决方案以外,还有没有其他的呢。答案是肯定的。有一种充分利用了static 初始化的特点的方法,叫做initialization on demand holder idiom.wikipedia上又叫(The solution of Bill Pugh)

public class Singleton{
private static class SingletonHolder {
public static final Singleton aInstance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.aInstance;
}
}

看到这个解决方案不禁拍手叫妙,它既解决了懒惰初始化,又解决了线程安全。而且代码比之前用Double-checked locking看着舒服很多。

 

由于笔者水平有限,难免会有出错之处,欢迎大家批评指正。