当你想让一个多线程访问一个成员变量但不需要复合原子性(不确定这是否是正确的术语)时,基本上就可以使用它。
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,因为它可能导致讨厌的bug(由构造不完全的对象等引起)。 )。
如果将volatile
关键字放在不需要的地方volatile
,则不会影响代码的正确性(即行为不会改变)。在性能方面,这将取决于JVM的实现。从理论上讲,由于编译器无法进行重新排序优化,必须使cpu缓存无效等原因,性能可能会略有下降,但是编译器可以再次证明你的字段无法被多个线程访问并消除volatile
关键字的影响完全并将其编译为相同的指令。
编辑: 对此评论的答复:
好的,但是为什么我们不能使今天的温度同步并为温度创建同步的吸气剂?
你可以,它将正常运行。你可以使用任何volatile
方法完成操作synchronized
,反之亦然。volatile
如果可以的话,你可能有两个原因: