java中volatile关键字最简单易懂的例子
我正在阅读Java中的volatile关键字,并完全理解它的理论部分。
但是,我正在寻找的是一个很好的案例,它显示了如果变量不是不稳定的,如果是变化的话会发生什么。
下面的代码片段不能按预期工作(来自aioobe)
class Test extends Thread {
boolean keepRunning = true;
public void run() {
while (keepRunning) {
}
System.out.println("Thread terminated.");
}
public static void main(String[] args) throws InterruptedException {
Test t = new Test();
t.start();
Thread.sleep(1000);
t.keepRunning = false;
System.out.println("keepRunning set to false.");
}
}
理想情况下,如果keepRunning不是不稳定的,线程应该继续不断地运行。 但是,它在几秒钟后停止。
我有两个基本问题: -
易失性 - >保证可见性而不是原子性
同步(锁定) - >保证可视性和原子性(如果正确)
挥发性不能代替同步
只有在更新引用并且不对其执行其他操作时才使用volatile。
例:
volatile int i = 0;
public void incrementI(){
i++;
}
不会使用同步或AtomicInteger线程安全,因为递增是一个复合操作。
为什么程序不能无限期地运行?
那要看各种情况。 在大多数情况下,JVM足够聪明地刷新内容。
正确使用volatile会讨论volatile的各种可能用法。 正确使用volatile会比较棘手,我会说“如果有疑问,请不要使用它”,而是使用synchronized块。
也:
可以使用同步块代替易失性,但反过来不正确 。
举个例子:如果没有声明为volatile,服务器JVM可以将keepRunning
变量提取出循环,因为它没有在循环中修改(将其变为无限循环),但客户机JVM不会。 这就是为什么你看到不同的结果。
有关易失变量的一般说明如下:
当一个字段被声明为volatile
,编译器和运行时会注意到这个变量是共享的,并且它的操作不应该与其他内存操作重新排序。 易失性变量不会缓存在寄存器或高速缓存中,因为它们对其他处理器是隐藏的,因此读取volatile变量总是会返回任何线程的最新写入 。
易失性变量的可见性影响超出了易失性变量本身的价值。 当线程A写入一个易失性变量,随后线程B读取该变量时,写入易失性变量之前A对A可见的所有变量的值在读取volatile变量后对B变得可见
volatile变量最常用的用法是完成,中断或状态标志:
volatile boolean flag;
while (!flag) {
// do something untill flag is true
}
易失变量可用于其他类型的状态信息,但尝试此操作时需要更多注意。 例如,除非可以保证变量只能从单个线程写入,否则volatile的语义不足以使增量操作( count++
)为原子。
锁定可以保证可见性和原子性; volatile变量只能保证可见性。
只有满足以下所有条件时,才可以使用易失性变量:
调试提示 :确保在调用JVM时始终指定-server JVM命令行开关,即使是开发和测试。 服务器JVM执行比客户端JVM更多的优化,例如将变量从循环中提取出来,这些循环未在循环中修改; 可能在开发环境(客户端JVM)中工作的代码可能会在部署环境(服务器JVM)中崩溃。
这是Java Concurrency in Practice的摘录,您可以找到关于此主题的最佳书籍。
我稍微修改了你的例子。 现在使用keepRunning作为volatile和non volatile成员的例子:
class TestVolatile extends Thread{
//volatile
boolean keepRunning = true;
public void run() {
long count=0;
while (keepRunning) {
count++;
}
System.out.println("Thread terminated." + count);
}
public static void main(String[] args) throws InterruptedException {
TestVolatile t = new TestVolatile();
t.start();
Thread.sleep(1000);
System.out.println("after sleeping in main");
t.keepRunning = false;
t.join();
System.out.println("keepRunning set to " + t.keepRunning);
}
}
链接地址: http://www.djcxy.com/p/16363.html
上一篇: Simplest and understandable example of volatile keyword in java