单例模式¶
双重检查锁定模式¶
双重检查锁定模式首先验证锁定条件(第一次检查),只有通过锁定条件验证才真正的进行加锁逻辑并再次验证条件(第二次检查)。
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) { // 第一次检查
synchronized(this) {
if (helper == null) { // 第二次检查
helper = new Helper();
}
}
}
return helper;
}
}
volatile 关键字保证多个线程可以正确处理单件实例。
volatile主要有以下两个功能:
- 保证变量的**内存可见性**
- 禁止volatile变量与普通变量**重排序**(JSR133提出,Java 5 开始才有这个“增强的volatile内存语义”)
内存可见性,指的是线程之间的可见性,当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值。
当一个线程对
volatile
修饰的变量进行**写操作**时,JMM会立即把该线程对应的本地内存中的共享变量的值刷新到主内存;当一个线程对volatile
修饰的变量进行**读操作**时,JMM会立即把该线程对应的本地内存置为无效,从主内存中读取共享变量的值。为优化程序性能,对原有的指令执行顺序进行优化重新排序。重排序可能发生在多个阶段,比如编译重排序、CPU重排序等。
如果 volatile 变量与普通变量发生重排序,虽然 volatile 变量能保证内存可见性,也可能导致普通变量读取错误。
惰性初始化模式¶
如果 helper
对象是静态的(每个类只有一个), 可以使用惰性初始化模式。
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}
这是因为内部类直到他们被引用时才会加载。