单例模式的双层监测机制

class SingleTon {

  private static volatile SingleTon instance;

  private SingleTon() {
  }

  public SingleTon getInstance() {
  if(instance == null) {
    synchronized(SingleTon.class) {
      if(instance == null) {
        instance = new SingleTon();
      }
    }
  return instance;
  }
}

两次检测instance == null,一次在同步块外,一次在同步块内

** 为什么在同步块内还要再检验一次? **
因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例了。

instance == new SingleTon()不是一句原子操作,JVM中对这句话的反应是下面三个:

  1. 给instance分配内存
  2. 调用SingleTon构造函数初始化成员变量
  3. 将instance对象指向分配的内存空间(执行完这一步instance变为非null)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(指向的内存空间,但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。
所以,需要将instance声明为volatile

有些人认为使用 volatile 的原因是可见性,也就是可以保证线程在本地不会存有 instance 的副本,每次都是去主内存中读取。但其实是不对的。使用 volatile 的主要原因是其另一个特性:禁止指令重排序优化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容