为什么在同步块中使用volatile?
我在java中看到了一些例子,他们在代码块上进行同步以更改某个variables,而该variables最初被声明为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; }
提前致谢。
在这种情况下,如果第一次检查在同步块内,同步本身就足够了(但如果variables不是易失性的,它不是一个线程可能看不到另一个线程执行的更改)。 单独挥发是不够的,因为您需要以primefaces方式执行多个操作。 但要小心! 你在这里有什么是所谓的双重locking – 一个常见的习惯用语,不幸的是不能可靠地工作 。 我认为这已经改变了自Java 1.6以来,但这种代码仍然可能是有风险的。
编辑 :当variables是易失性的,这个代码工作正常,因为JDK 5(不是我之前写的6),但它不会按预期在JDK 1.4或更早版本下工作。
这使用双重检查的locking,请注意if(uniqueInstance == null)
不在同步部分内。
如果uniqueInstance
不是易失性的,则可能会使用部分构造的对象“初始化”,其中部分构build的对象除了在synchronized
块中执行的线程之外,其中的部分内容不可见。 在这种情况下,volatile会使其成为全部或全部操作。
如果你没有同步块,你最终可能会有两个线程同时到达这一点。
if(uniqueInstance == null) { uniqueInstance = new someClass(); <---- here
而且你构造了2个SomeClass对象,这打破了目的。
严格地说,你不需要不稳定,方法本来可以
public static someClass getInstance() { synchronized(FullDictionary.class) { if(uniqueInstance == null) { uniqueInstance = new someClass(); } return uniqueInstance; } }
但是,这会导致执行getInstance()的每个线程的同步和序列化。
这篇文章解释了挥发性背后的想法。
在开创性工作Java实践中的并发性中也谈到了这一点。
其主要思想是并发性不仅涉及到共享状态的保护,而且还涉及线程之间状态的可见性:这就是易失性进入的地方(这个较大的合同是由Java内存模型定义的)。
您可以不使用同步块进行同步。 这是没有必要使用volatilevariables…易失性更新从主内存中的一个variables..并同步更新已经从主内存访问所有共享variables..所以你可以使用它根据您的要求..
我的两毛钱在这里
首先简单介绍一下这段代码的直觉
if(uniqueInstance == null) { synchronized(someClass.class) { if(uniqueInstance == null) { uniqueInstance = new someClass(); } } }
它检查uniqueInstance == null两次的原因是为了减less调用相对较慢的synchronized块的开销。 所谓的双重locking。
其次,它使用同步的原因很容易理解,它使同步块内的两个操作primefaces化。
最后,volatile修饰符确保所有线程都看到相同的副本,所以同步块之外的第一个检查将以与同步块“同步”的方式看到uniqueInstance的值。 如果没有volatile修饰符,则一个线程可以为uniqueInstance赋值,但另一个线程可能不会通过第一次检查来看到它。 (虽然第二次检查会看到它)