你基本上使用它,当你想让一个成员变量被多个线程访问,但不需要复合原子性(不知道这是否是正确的术语)。
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?事实上,你可以(在某种意义上,你的程序不会爆炸或某物),但是其他线程的温度变化可能或可能不是“可见”的主线程。
基本上这意味着,甚至可能是你的应用程序。保持写入如果你不使用volatile,今天的温度永远是0(在实践中,值往往会变得最终可见。但是,你不应该冒险在必要时不使用volatile,因为它可能导致讨厌的错误完全构造的对象等)。
如果你把volatile关键字放在不需要volatile的东西上,它不会影响你的代码的正确性(即行为不会改变)。在性能方面,它将取决于JVM实现。在理论上你可能会得到一个微小的性能退化,因为编译器不能做重排序优化,必须使CPU缓存等无效,但然后再次编译器可以证明你的字段不能被多个线程访问,并消除volatile关键字,并将其编译为相同的指令。
编辑:
回复此评论:
Ok, but why can’t we make todaysTemperature synchronized and create a synchronized getter for temperature?
你可以和它的行为正确。任何你可以与volatile可以做同步,但不是相反。如果您可以:
>更少的bug容易:这取决于上下文,但在许多情况下使用volatile不容易发生并发错误,例如阻塞锁定,死锁等。>更高性能:在大多数JVM实现中,volatile可以具有显着更高的吞吐量和更好的延迟。然而,在大多数应用中,差异太小而不重要。