首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Java中最简单易懂的volatile关键字示例

Java中最简单易懂的volatile关键字示例
EN

Stack Overflow用户
提问于 2013-07-19 22:02:34
回答 12查看 79.7K关注 0票数 83

我正在阅读Java中的volatile关键字,并且完全理解它的理论部分。

但是,我正在寻找的是一个很好的例子,它展示了如果变量不是易失性的,以及如果变量是易失性的,会发生什么。

下面的代码片段不能按预期工作(取自here):

代码语言:javascript
复制
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吗?
  • 不是同步的易失性替代品吗?它实现原子性了吗?
EN

回答 12

Stack Overflow用户

发布于 2013-07-19 22:07:05

Volatile -->保证可见性,而不是原子性

同步(锁定) -->保证可见性和原子性(如果处理得当)

不能替代同步Volatile

仅当更新引用且不对其执行某些其他操作时,才使用volatile。

示例:

代码语言:javascript
复制
volatile int i = 0;

public void incrementI(){
   i++;
}

如果不使用同步或AtomicInteger,将不是线程安全的,因为递增是一种复合操作。

为什么程序不能无限期运行?

嗯,这取决于不同的情况。在大多数情况下,JVM足够智能,可以刷新内容。

Correct use of volatile讨论了易失性的各种可能的用法。正确地使用易失性是很棘手的,我会说“如果有疑问,就省略它”,使用同步化的块。

另外:

同步块可以用来代替,但反之不是真的volatile。

票数 53
EN

Stack Overflow用户

发布于 2013-07-19 22:24:12

对于您的特定示例:如果没有声明为,服务器JVM可以将keepRunning变量提升到循环之外,因为它没有在循环中修改(将其变成无限循环),但是客户机JVM不会。这就是为什么你会看到不同的结果。

关于易失性变量的一般解释如下:

当一个字段被声明为volatile时,编译器和运行时会注意到这个变量是共享的,对它的操作不应该与其他内存操作一起重新排序。易失性变量不会缓存在寄存器或缓存中,因为它们对其他处理器是隐藏的,因此读取易失性变量总是返回任何线程的最新写入。

易失性变量的可见性影响超出了易失性变量本身的值。当线程A写入易失性变量,随后线程B读取该变量时,在写入易失性变量之前对A可见的所有变量的值在读取易失性变量之后对B变为可见。

易失性变量最常见的用途是作为完成、中断或状态标志:

代码语言:javascript
复制
  volatile boolean flag;
  while (!flag)  {
     // do something untill flag is true
  }

易失性变量可以用于其他类型的状态信息,但在尝试此操作时需要更加小心。例如,易失性的语义不足以使增量操作(count++)成为原子,除非您能保证变量只从单个线程写入。

锁可以同时保证可见性和原子性;易失性变量只能保证可见性。

只有在满足以下所有条件时,才能使用易失性变量:

  • 写入变量不依赖于它的当前值,或者您可以确保只有一个线程更新该值;
  • 该变量不会与其他状态变量一起参与不变量;在访问该变量时,由于任何其他原因,不需要
  • 锁定。

调试技巧:确保在调用-server时指定JVM命令行开关,即使是在开发和测试时也是如此。服务器JVM比客户端JVM执行更多的优化,比如将未在循环中修改的变量提升到循环之外;在开发环境(客户端JVM)中看起来可以工作的代码可能会在部署环境(服务器JVM)中中断。

这是你能找到的关于这个主题的最好的书"Java Concurrency in Practice"的摘录。

票数 29
EN

Stack Overflow用户

发布于 2014-01-05 18:02:52

我稍微修改了一下你的例子。现在使用keepRunning作为易失性和非易失性成员的示例:

代码语言:javascript
复制
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);
    }
}
票数 17
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17748078

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档