单例模式中的双重校验锁
// 单线程的时候 class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { helper = new Helper(); } return helper; } }
这段在使用多线程的情况下无法正常工作。在多个线程同时调用getHelper()
时,必须要获取锁,否则,这些线程可能同时去创建对象,或者某个线程会得到一个未完全初始化的对象。
锁可以通过代价很高的同步来获得,就像下面的例子一样。
//这样写虽然正确,但是粗暴地把getHelper锁住了,这样代价很大 class Foo { private Helper helper = null; public synchronized Helper getHelper() { if (helper == null) { helper = new Helper(); } return helper; } }
只有getHelper()
的第一次调用需要同步创建对象,创建之后getHelper()
只是简单的返回成员变量,而这里是无需同步的。 由于同步一个方法会降低100倍或更高的性能[2], 每次调用获取和释放锁的开销似乎是可以避免的:一旦初始化完成,获取和释放锁就显得很不必要。许多程序员一下面这种方式进行优化:
- 检查变量是否被初始化(不去获得锁),如果已被初始化立即返回这个变量。
- 获取锁
- 第二次检查变量是否已经被初始化:如果其他线程曾获取过锁,那么变量已被初始化,返回初始化的变量。
- 否则,初始化并返回变量。
//这种双重检查锁定就很好地解决问题,避免每次调用都要获取锁 class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null) { helper = new Helper(); } } } return helper; } }
代码都是乱鸠写的