Dor*_*ham 42 java multithreading volatile synchronized double-checked-locking
我在java中看到了一些例子,他们在一块代码上做同步来改变一些变量,而这个变量最初被声明为volatile.我在单例类的一个例子中看到,他们将唯一的实例声明为volatile并且他们同步了块初始化那个实例...我的问题是为什么我们在同步它时声明它是不稳定的,为什么我们需要同时做两个?对其他人来说不是其中之一吗?
public class someClass {
volatile static uniqueInstance = null;
public static someClass getInstance() {
if(uniqueInstance == null) {
synchronized(someClass.class) {
if(uniqueInstance == null) {
uniqueInstance = new someClass();
}
}
}
return uniqueInstance;
}
Run Code Online (Sandbox Code Playgroud)
提前致谢.
Mic*_*ski 19
在这种情况下,如果第一次检查在同步块内,则同步本身就足够了(但如果变量不是易失性的话,一个线程可能看不到另一个线程执行的更改).单独使用Volatile是不够的,因为您需要以原子方式执行多个操作.但要小心!你所拥有的是所谓的双重检查锁定 - 一种常见的习惯用法,遗憾的是它不能可靠地工作.我认为自Java 1.6以来这已经发生了变化,但这种代码仍然存在风险.
编辑:当变量是易失性时,此代码自JDK 5起正确工作(不像我之前写的那样6),但它在JDK 1.4或更早版本中无法正常工作.
这使用双重检查锁定,请注意它if(uniqueInstance == null)
不在同步部分内.
如果uniqueInstance
不是volatile,它可能会被部分构造的对象"初始化",其中部分对象不在synchronized
块中执行的线程可见.volatile在这种情况下使这成为全有或全无操作.
如果你没有synchronized块,那么最终可能会有2个线程同时到达这一点.
if(uniqueInstance == null) {
uniqueInstance = new someClass(); <---- here
Run Code Online (Sandbox Code Playgroud)
并且你构造了2个SomeClass对象,这违背了目的.
严格来说,你不需要挥发性,方法本来可以
public static someClass getInstance() {
synchronized(FullDictionary.class) {
if(uniqueInstance == null) {
uniqueInstance = new someClass();
}
return uniqueInstance;
}
}
Run Code Online (Sandbox Code Playgroud)
但是这会导致执行getInstance()的每个线程的同步和序列化.
这篇文章解释了 volatile 背后的想法。
它也在开创性工作Java Concurrency in Practice 中得到解决。
主要思想是并发不仅涉及共享状态的保护,还涉及线程之间该状态的可见性:这就是 volatile 的用武之地。(这个更大的契约由Java 内存模型定义。)