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吗? 不符合JLS的理论。
  • 挥发替代品是否同步? 它是否实现原子性?

  • 易失性 - >保证可见性而不是原子性

    同步(锁定) - >保证可视性和原子性(如果正确)

    挥发性不能代替同步

    只有在更新引用并且不对其执行其他操作时才使用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

    下一篇: HashMap Java 8 implementation