本问题已经有最佳答案,请猛点这里访问。
我读过"何时使用Java中的‘易失性’?"但我还是很困惑。如何知道何时标记变量volatile?如果我弄错了,要么在需要它的东西上省略一个易失性,要么在不需要它的东西上加上易失性?在确定多线程代码中哪些变量应该是易失变量时,经验法则是什么?
可能是"易失性"的重复?您是否曾经使用Java中的易失性关键字?,使用volatile关键字等等。
stackoverflow.com/questions/106591/…以及
锁提供两个主要功能:互斥和可见性。可变变量共享同步的可见性特性,但不共享原子性特性。阅读更多(布莱恩·戈茨)。
"没有原子性特性"…除了8字节基元的长和双字节的易失性声明使单个读原子性或单个写原子性。这是小规模的原子性。需要同步块才能具有大规模原子性。
你基本上使用它,当你想让一个成员变量是由多线程访问,但不需要的化合物的原子性(不确定,如果这是正确的术语)。
class BadExample {
private volatile int counter;
public void hit(){
/* This operation is in fact two operations:
* 1) int tmp = this.counter;
* 2) this.counter = tmp + 1;
* and is thus broken (counter becomes fewer
* than the accurate amount).
*/
counter++;
}
}
上面的例子是一个浴室,因为你需要化合物的原子性。
class BadExampleFixed {
private int counter;
public synchronized void hit(){
/*
* Only one thread performs action (1), (2) at a time
*"atomically", in the sense that other threads can not
* observe the intermediate state between (1) and (2).
* Therefore, the counter will be accurate.
*/
counter++;
}
}
现在一个有效的例子:
class GoodExample {
private static volatile int temperature;
//Called by some other thread than main
public static void todaysTemperature(int temp){
// This operation is a single operation, so you
// do not need compound atomicity
temperature = temp;
}
public static void main(String[] args) throws Exception{
while(true){
Thread.sleep(2000);
System.out.println("Today's temperature is"+temperature);
}
}
}
现在,为什么你不能只是使用private static int temperature?事实上,你可以(在对方,你的程序不会吹什么的),但由其他线程temperature变更到可能或不可能是"可见"的主线程。
这种均值,它甚至可能是你的应用程序。如果你的写作是一个Today's temperature is 0永远不要使用volatile(在实践中,对飞机最终可见的价值。然而,你不应该不必要的波动风险时使用,因为它可以导致讨厌的臭虫(引起由在完全重构对象等。
如果你把东西在volatile关键字不需要volatile,它不会影响您的代码的正确性(即行为不会改变)。性能方面,它想depend on the JVM的实现。你可能得到一个小的理论性能退化,因为编译器不能做reordering optimisations,要invalidate CPU缓存等,然后再在编译器可以证明你有多场无法加载和删除的线程访问的影响和它的volatile关键字完全相同的编译指示。
编辑:本评论反应:
Ok, but why can't we make todaysTemperature synchronized and create a synchronized getter for temperature?
你想和它的正确的行为。的东西,你可以与volatileCAN和synchronized,但不反之亦然。你可能有两个原因:如果你喜欢volatileCAN
不易:这取决于错误的上下文,但在许多情况下是不使用一个volatile易并发错误,样的阻塞而控股锁,死锁等。
在大多数的JVM的实现更多的性能:volatileCAN有显着高,高通量和更好的延迟。然而,在大多数应用差别太小到物。
我经常看到这个问题,这是第一个对我完全有意义的答案。谢谢您。
好吧,但是为什么我们不能使todaysTemperature同步,并为temperature创建一个同步getter呢?
@塞米扬达尼洛夫:在底部添加了一个答案。Hth.
对我来说,还不清楚同步getter在这里有什么帮助?如果不使用volatile,我们仍然无法保证线程本地缓存。
@圣安塔里奥:synchronized提供的担保是volatile提供的担保的超集,因此除了synchronized之外,使用volatile是多余的。
是的,我已经问过了。
进一步巩固这一工作原理:定义一个不稳定和最终的字段是一个编译错误。这没有任何意义,因为无法重新分配最终变量。
@恩诺肖吉:我搜索了原子化合物的说法,但还是不理解这个概念。你能解释一下你在这里所说的原子化合物是什么意思吗?
AkshayrajKore,当他说"复合原子性"时,他指的是需要作为一个整体的原子性的多个操作。例如,您放入同步方法或同步块中的所有代码。另一个例子是i++,因为它是3个操作:读、增量、写。把i++放在一个同步块中,使3个操作作为一个整体成为原子操作。
波动是最有用的在无锁算法。你的共享可变数据作为标记持有挥发性当你不使用锁定访问变量的变化是由一个和你想在另一个线程是可见的,或你想创建一个"后发生的"关系到确保不重新计算一次,确保有序,在可见的变化。适当的时间。
《JMM食谱是运营了CAN的序,无法。
volatile关键字保证了volatile变量的值总是从主内存中读取,而不是从线程的本地缓存中读取。
从Java并发教程:
Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable
号
这意味着对可变变量的更改对于其他线程总是可见的。它还意味着,当线程读取可变变量时,不仅可以看到可变变量的最新变化,还可以看到导致变化的代码的副作用。
关于您的查询:
How do I know when I should mark a variable volatile? What are the rules of thumb when figuring out what variables should be volatile in multithreaded code?
号
如果您觉得所有的读线程总是得到一个变量的最新值,那么您必须将该变量标记为volatile。
如果有一个编写器线程要修改变量的值,而有多个编写器线程要读取变量的值,那么volatile修饰符可以保证内存的一致性。
如果您有多个线程来写入和读取变量,那么仅使用volatile修饰符并不能保证内存的一致性。您必须使用synchronize代码或使用高级并发构造,如Locks、Concurrent Collections、Atomic variables等。
相关SE问题/文章:
Java文档中易失性变量的解释
Java中易失性与同步性的区别
javarevisited文章
在volatile也可以用来安全地发布不可变的对象在多线程环境。
这是一场declaring样public volatile ImmutableObject foosecures所有目前可用的线程总是湖实例参考。
Java并发湖在实践上更多的主题。
所以我认为这个"不变的"部分有点可疑…请参阅我的答案:StAdvObjult.com /问题/ 3964317 //Helip;JSR-133常见问题解答,项目C.UMD.EDU/~PUGH/Java/MeMyMy/JSR-133-FAQ。我认为更准确的说法是,您可以安全地发布不需要其他同步的对象…我可能没有正确的意识到这一点,但是只要一个不稳定的写入发生,在同一线程中正常的写入,在同一线程之前,任何线程读取都可以看到。
@Andersoj是正确的,波动性是可传递的。A.B的读数不稳定是A的读数不稳定。如果A.B以程序顺序位于A.B之前,则A.B的写入发生在A的写入之前。因此,volatile可以用于安全地发布可变(否则是非线程安全)对象。我不认为这是一个特别好的想法,总是尽可能地宣扬不变的事物,但它仍然是一个重要的点。
实际上不同意上面的投票答案中给出的例子,据我所知,它没有正确地说明Java内存模型中的易失性语义。volatile的语义更加复杂。
在所提供的示例中,主线程可以永远继续打印"今天的温度为0",即使有另一个线程正在运行,如果另一个线程从未被调度过,则应该更新该温度。
用2个变量来说明易失性语义是一种更好的方法。
为了简单起见,我们假设更新这两个变量的唯一方法是通过方法"setttemperatures"。
为了简单起见,我们假设只有2个线程在运行,即主线程和线程2。
//volatile variable
private static volatile int temperature;
//any other variable, could be volatile or not volatile doesnt matter.
private static int yesterdaysTemperature
//Called by other thread(s)
public static void setTemperatures(int temp, int yestemp){
//thread updates yesterday's temperature
yesterdaysTemperature = yestemp;
//thread updates today's temperature.
//This instruction can NOT be moved above the previous instruction for optimization.
temperature = temp;
}
最后两个分配指令不能为优化目的被编译器、运行时或硬件重新排序。
public static void main(String[] args) throws Exception{
while(true){
Thread.sleep(2000);
System.out.println("Today's temperature is"+temperature);
System.out.println("Yesterday's temperature was"+yesterdaysTemperature );
}
}
号
一旦主线程读取挥发性可变温度(在打印过程中)。
1)可以保证它将看到这个易失性变量的最近写入值,不管向它写入多少线程,不管它们在哪个方法中更新、同步或不同步。
2)如果主线程中的system.out语句运行,在线程2运行语句temperature=temp的瞬间之后,昨天的温度和今天的温度都将保证在线程2运行语句temperature=temp时打印线程2设置的值。
如果a)多个线程正在运行,b)除了settemperatures方法外,还有其他方法可以更新变量昨天的温度和今天的温度,这些其他线程正在主动调用这些变量。我认为,根据Java内存模型如何描述易失性语义,将需要一个体面的文章来分析其含义。
简而言之,尝试只使用volatile进行同步是非常危险的,最好还是坚持同步方法。
您的示例还可以用来解释在哪里使用synchronized可能更好。当另一个线程执行"yesterdaystemperature=yestemp"但尚未执行"temperature=temp"时,可以运行这些print语句,不是吗?
mindprod.com http:/ / / / volatile.html jgloss
"在这动荡的关键字是特定变量可能被修改由其他线程。
"因为其他线程本地变量不能湖,那里没有任何需要标记的本地变量波动。你需要同步到坐标改变变量的波动往往从不同的线程,但想做只是看看他们。"
ValTale:意味着不断改变值。这个变量的值永远不会在本地缓存线程:所有的读写都直接进入"主内存"。换句话说,Java编译器和线程不缓存这个变量的值,并且总是从主内存读取它。