实际上不同意票数最高的答案中给出的示例,据我所知,它确实不根据 Java 内存模型正确地说明了易失性语义。 Volatile 具有更复杂的语义。
在提供的示例中,主线程可以继续打印“今天的温度为 0”,即使有另一个线程正在运行,如果该线程从未被调度,则应该更新温度。
说明 volatile 语义的更好方法是使用 2 个变量。
为简单起见,我们假设更新这两个变量的唯一方法是通过方法 "setTemperatures"。
为简单起见,我们假设只有 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 );
}
}
一旦主线程读取到volatile变量温度(在打印的过程中),
1) 保证它会看到这个 volatile 变量的最近写入 值,无论有多少线程正在写入它,无论它们使用哪种方法更新它,同步与否。
2) 如果主线程中的 system.out 语句运行,在线程 2 运行语句温度 = temp 的时刻之后,昨天的温度和今天的温度都将保证打印线程 2 在运行语句 temperature=temp 时在其中设置的值。
如果 a) 多个线程正在运行并且 b) 除了 setTemperatures 方法之外,还有其他方法可以更新变量昨天的温度和今天的温度,那么这种情况会变得更加复杂。由这些其他线程调用。我认为根据 Java 内存模型如何描述易失性语义来分析其含义需要一篇体面的文章。
简而言之,尝试仅使用 volatile 进行同步是非常冒险的,最好还是坚持同步你的方法。