java 单例模式中的双重检测为什么要加 volatile 关键字?-灵析社区

喝一杯吧可以吗

Java 实现单例模式有方法有双重检测锁,代码如下: public class Singleton { private static volatile Singleton singleton = null; private Singleton(){} public static Singleton getSingleton(){ if(singleton == null){ synchronized (Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } } return singleton; } } 我理解的 synchronized 关键字实现了可见性、原子性和有序性,临界区中的代码可以重排序,但是不能重排序到临界区外面,synchronized 实现的可见性是临界区中代码执行结束之后,里面的共享变量会刷新到主内存中,那么如果 new Singleton() 方法被拆成了三个操作,并且经过重排序之后的顺序是这样的话: 1. 分配内存 2. 将实例引用赋值给 singleton 变量 3. 实例初始化 不管这三个操作怎么重排序,另外一个线程看到的结果都是这三个操作执行完成后的结果(因为 synchronized 的原子性),那不就相当于另外一个线程访问到的 singleton 如果不为 null 的话就肯定实例化了吗?为什么还要多此一举加个 volatile 关键字禁止重排序呢?

阅读量:13

点赞量:0

问AI
我大概了解你的疑惑点,上面的评论其实已经可以解决你的疑惑了。 "另外一个线程访问到的 singleton 如果不为 null 的话就肯定实例化了吗" 问题的关键在于,Sychronized加锁的位置,它没有对getSingleton()整个方法解锁,而是判断singleton为null后才会去抢锁,所以多个线程可以同时进入getSingleton方法。 由于"synchronized" 的有序性是持有相同锁的两个同步块只能串行的进入,即被加锁的内容要按照顺序被多个线程执行,但是其内部的同步代码还是会发生重排序 ,使块与块之间有序可见。 那么如果不加上volatile防止指令的重排序,new Singleton() 方法被拆成了三个操作,并且经过重排序之后的顺序是这样的话: 1. 分配内存 2. 将实例引用赋值给 singleton 变量 3. 实例初始化 其中线程A假设在sychronized块中将内存地址赋值给了对象,"其他线程此时调用getSingleton()",发现singleton此时不为空了,那么直接返回singleton,但是此时singleton"还未完成初始化",那么问题就出现了。