3 回答
TA贡献1852条经验 获得超1个赞
当你想让一个成员变量被多个线程访问但不需要复合原子性(不确定这是否是正确的术语)时,你基本上使用它。
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
关键字放在不需要的东西上volatile
,它不会影响代码的正确性(即行为不会改变)。在性能方面,它将取决于JVM实现。从理论上讲,由于编译器无法进行重新排序优化,不得不使CPU缓存等无效,因此可能会出现微小的性能下降,但再次编译器可以证明多个线程无法访问您的字段并删除volatile
关键字的影响完全并将其编译为相同的指令。
编辑:
回复此评论:
好的,但是为什么我们不能让todaysTemperature同步并为温度创建一个同步的getter?
你可以,它会表现正常。您可以使用的任何东西volatile
都可以完成synchronized
,但反之亦然。volatile
如果可以,您可能会有两个理由:
减少错误:这取决于上下文,但在许多情况下使用
volatile
不太容易出现并发错误,例如在持有锁时阻塞,死锁等。性能
volatile
更高:在大多数JVM实现中,可以具有更高的吞吐量和更好的延迟。然而,在大多数应用中,差异太小而无关紧要。
TA贡献1829条经验 获得超9个赞
volatile
关键字保证volatile变量的值总是从主内存中读取,而不是从Thread的本地缓存中读取。
来自java并发教程:
使用volatile变量可以降低内存一致性错误的风险,因为对volatile变量的任何写入都会建立与之后读取相同变量的先发生关系
这意味着对volatile变量的更改始终对其他线程可见。它还意味着当线程读取volatile变量时,它不仅会看到volatile的最新更改,还会看到导致更改的代码的副作用。
关于你的查询:
我怎么知道何时应该标记变量volatile?在确定多线程代码中哪些变量应该是易变的时,有哪些经验法则?
如果您认为所有读者线程始终获得变量的最新值,则必须将变量标记为 volatile
如果您有一个编写器线程来修改变量和多个读取器线程的值来读取变量的值,那么volatile修饰符可以保证内存的一致性。
如果您有多个线程来写和读取变量,volatile
单独使用修饰符并不能保证内存一致性。你必须synchronize
在代码或使用高水平并发结构,如Locks
,Concurrent Collections
,Atomic variables
等。
添加回答
举报