在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销。双重检查锁定是常见的延迟初始化技术,但它是一个错误的用法。本文将分析双重检查锁定的错误根源 。
双重检查锁定的由来
在Java程序中,有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化(单例)。此时程序员可能会采用延迟初始化。但要正确实现线程安全的延迟初始化需要一些技巧,否则很容易出现问题。
非线程安全实现:
public class UnsafeLazyInitialization {
private static Instance instance;
public static Instance getInstance() {
if(instance == null) { // 1:A线程执行
instance = new Instance(); // 2:B线程执行
}
return instance;
}
}
在UnsafeLazyInitialization类中,假设A线程执行代码1的同时,B线程执行代码2。此时,线程A可能会看到instance引用的对象还没有完成初始化 ( 原因在后边 )。
线程安全的实现(低性能):
对于UnsafeLazyInitialization类中,我们可以对getInstance()方法做同步处理来实现线程安全的延迟初始化。示例代码如下。