Java中volatile关键字的最简单和易懂的例子

9 浏览
0 Comments

Java中volatile关键字的最简单和易懂的例子

我正在阅读有关Java中volatile关键字的内容,并完全理解了它的理论部分。\n但是,我正在寻找的是一个好的案例示例,展示了如果变量不是volatile以及如果是volatile会发生什么。\n下面的代码片段(摘自这里)不按预期工作:\n

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.");
    }
}

\n理想情况下,如果keepRunning不是volatile,线程应该无限期地继续运行。但是,它在几秒钟后停止了。\n我有两个基本问题:\n

    \n

  • 有人能用示例解释一下volatile吗?不要用JLS的理论。
  • \n

  • volatile是同步的替代品吗?它能实现原子性吗?
  • \n

0
0 Comments

简单而易懂的Java中volatile关键字的例子

在上述代码中,通过将keepRunning声明为volatile或非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);
    }
}

这个例子非常好,它在我这里完美地工作。如果keepRunning没有被标记为volatile,线程将永远挂起。一旦将keepRunning标记为volatile,它在t.keepRunning = false;之后停止。

这个例子对我有用,我一直在寻找一个有效的例子。加1,因为它帮助了我,而且缺乏解释也没有造成任何伤害,也不值得被贬低。

嗨,paritosht和Doe,你们能解释一下为什么你们的代码是一个有效的例子吗?当我的机器执行问题中提供的代码时,无论是否使用volatile关键字,它都会停止。

我在这里使用votalite得到了相同的结果,无论是否使用。

0
0 Comments

volatile关键字在Java中的最简单和易懂的示例是什么?

volatile关键字主要用于确保多线程环境下变量的可见性。当一个变量被声明为volatile时,编译器和运行时会知道这个变量是共享的,对它的操作不应该与其他内存操作重新排序。volatile变量不会被缓存在寄存器或缓存中,因此对volatile变量的读操作总是返回任何线程最近写入的值。

具体到你的例子,如果没有声明volatile,服务器JVM可能会将keepRunning变量提升到循环外部,因为它在循环内部没有被修改(变成无限循环),但客户端JVM不会这样做。这就是为什么你会看到不同的结果。

volatile变量的可见性效果超出了volatile变量本身的值。当线程A写入一个volatile变量,然后线程B读取同一个变量时,线程A在写入volatile变量之前对所有变量的值对线程B在读取volatile变量之后都是可见的。

volatile变量最常用于作为完成、中断或状态标志:

volatile boolean flag;
while (!flag)  {
   // 当flag为true之前一直执行某些操作
}

volatile变量也可以用于其他类型的状态信息,但在尝试这样做时需要更加小心。例如,volatile的语义不足以使自增操作(count++)成为原子操作,除非你可以保证该变量只从一个线程写入。

锁可以保证可见性和原子性;而volatile变量只能保证可见性。

只有满足以下所有条件时,才能使用volatile变量:

- 对变量的写入不依赖于其当前值,或者你可以确保只有一个线程更新该值;

- 该变量不与其他状态变量共同参与不变式;

- 在访问该变量时不需要任何其他原因的锁定。

调试提示:在调用JVM时,无论是开发还是测试,都务必指定-server JVM命令行开关。服务器JVM比客户端JVM执行更多的优化,例如将在循环中未修改的变量提升出来;在开发环境(客户端JVM)中可能正常工作的代码可能在部署环境(服务器JVM)中出现问题。

这是《Java并发实战》一书的摘录,这本书是你在这个主题上可以找到的最好的书。

0
0 Comments

在Java中,volatile关键字用来确保可见性,但不保证原子性。而同步(加锁)可以保证可见性和原子性(如果使用正确)。因此,volatile不能替代同步。

只有在更新引用而不进行其他操作时,才应该使用volatile。如下所示的例子中,如果没有使用同步或AtomicInteger来进行操作,那么incrementI方法就不是线程安全的,因为增加操作是一个复合操作。

volatile int i = 0;
public void incrementI(){
   i++;
}

为什么程序不会无限运行呢?这取决于各种情况。在大多数情况下,JVM足够聪明,会刷新内存中的内容。

《使用volatile的正确方式》讨论了volatile的各种可能用法。正确使用volatile是棘手的,我会说"当你怀疑时,不要使用它",而是使用同步块。

另外,同步块可以替代volatile,但反过来则不成立。这是错误的。volatile保证了原子性。Oracle的文档明确指出了这一点。

在Java中,当我们有多个线程时,每个线程都有自己的堆栈(内存空间),并且在初始化时,每个线程都有自己的变量副本可以访问。如果没有用volatile修饰int i,每个线程可以在其执行中使用它。当用volatile声明时,每个线程必须直接从/到主内存读取/写入i的值,而不是从/到局部副本读取/写入。因此,从每个线程的角度来看,对变量i的操作是原子的。

答案中关于原子性的部分是令人困惑的。同步给你提供了互斥访问和可见性。volatile只提供了可见性。此外,volatile使得对long和double的读/写操作是原子的(同步也通过其互斥性实现了原子性)。

volatile关键字保证了可见性,但不保证原子性。为了保证线程安全,应该使用同步或AtomicInteger来进行操作。

0