Skip to content

单例模式

双重检查锁定模式

双重检查锁定模式首先验证锁定条件(第一次检查),只有通过锁定条件验证才真正的进行加锁逻辑并再次验证条件(第二次检查)。

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;
    }
}

这是因为内部类直到他们被引用时才会加载。