java中volatile关键字的行为

时间:2012-03-15 14:15:29

标签: java multithreading

我需要一些关于Java线程的Volatile Keyword的例子。

根据volatile关键字的定义,当变量被声明为volatile时,线程将直接读/写到变量内存,而不是从本地线程缓存读/写。

如果我错了,请纠正我。

因此,当我运行以下程序时,理解

public class ThreadRunnableBoth implements Runnable{  
    private volatile int num =0;  

    public void run(){  
        Thread t = Thread.currentThread();  
        String name = t.getName();

        for(int i=0; i<100; i++){
            if(name.equals("Thread1")){
                num=10;
                System.out.println("value of num 1 is :"+num);
            }else{
                num=15;
                System.out.println("value of num 2 is :"+num);  
            }  
        }

    }  
    public static void main(String args[]) throws InterruptedException{  
        Runnable r = new ThreadRunnableBoth();  
        Thread t1 = new Thread(r);  
        t1.setName("Thread1");

        Thread t2 = new Thread(r);  
        t2.setName("Thread2");  

        t1.start();  
        t2.start();  

    }  
}  

我从某个网站获得了这些示例,当我尝试运行它时,我发现删除Volatile或添加Volatile Keyword没有任何区别。

请解释我删除它并添加它的区别。

非常感谢。

6 个答案:

答案 0 :(得分:3)

使用volatile关键字之间的主要区别在于您是否需要内存栅栏来安全地处理数据。

内存防护可防止由于无序执行而导致多个线程之间可能出现的副作用。通过指示CPU,编译器/运行时环境可以告诉CPU在不破坏程序正确性的情况下不能操作读取的原始排序约束。

Read up on memory fences here,请记住解决方案的关键是一致性,不是位置。读取请求可以在缓存处停止,提供缓存保证一致(通过CPU的内部机制)。

答案 1 :(得分:0)

所以没有 volatile关键字,线程只是在其本地内存缓存中打印num的值。他们对num的更改绝不会与num的其他主题视图同步。我看到输出如:

value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
...

使用volatile,它们都会更新并打印到同一个全局存储位置,并在set / get周围设置内存屏障。但这不会改变输出,这非常受竞争条件的影响。我看到输出如:

value of num 2 is :15
value of num 1 is :15
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
...

当打印值时,套装在最后一次比赛。

您可能没有看到此输出,因为您的处理器体系结构或JRE仅在IO事件上进行上下文切换,或者不提供完整的线程执行。如果你显示一些输出,那么我可以再评论一下。

答案 2 :(得分:0)

  

根据volatile关键字的定义,它表示变量是   声明为volatile然后线程将直接读/写变量   内存而不是本地线程缓存的读/写。

不一定。支持cache coherence的系统可以使易失性字段保持最新状态,而无需从主内存中读取。 Volatile表示每个线程都会看到某个字段的最新值。

至于内存可见性,如果删除volatile但您的程序可能会失败,则不一定会立即看到任何更改。它运行的时间越长,你可能会看到的问题就越多。

答案 3 :(得分:0)

volatile变量的影响在multiprocessor系统上很明显,其中不同的线程在不同的处理器上运行。在普通的单处理器系统上,影响可能不明显。

此网站上的good discussion thread是同一主题。

答案 4 :(得分:0)

在您的示例中,num以默认值0开头,然后您(在其声明行上)将其分配给0。如果num不是volatile,那么这项任务就是数据竞赛,但当然你无法分辨出来。

然后你只在一个线程中使用num,并且在一个线程中你总会看到事情按照代码所说的顺序发生。因此,在这种情况下,num不必是易变的。

现在,如果您修改了main方法,以便在线程启动后检查t1.num(但没有检查它是否以创建前发生边缘的方式完成,如{ {1}}),如果没有Thread.join不稳定,您将会遇到数据争用。您可以num等待5天,但仍然无法保证将main视为num以外的任何内容。而且不仅如此,如果0还有一个非易失性ThreadRunnableBoth,其开始boolean并且在跳跃结束时设置为false true也可以看到mainboolean(因此意味着线程已经完成),但true仍然在num!这是一个数据竞争,并且可能发生在(例如)多核机器上,其中布尔值在0之前从本地寄存器中刷新。在此示例中,同时生成num和布尔num将确保volatile为真,boolean

但是这里是踢球者:即使没有num == 0 || num == 15关键字 - 也就是说,即使存在数据竞争 - 你也不会保证看到活泼的行为。也就是说,数据竞赛表明你无法保证你会在另一个主题中看到变化 - 但它并不能保证你不会获胜。可能是它在你的机器上运行了100次,然后有人把它放在一台8核机器上,它是一个更复杂的程序的一部分,所以它得到了不同的优化,然后是东西断裂。

答案 5 :(得分:0)

大部分讨论都与硬件有关。实际上编译器优化通常更相关。你是用一种小方法重复访问一个字段的,所以我们把它放在一个寄存器中。改变物理内存不会改变寄存器中的值。

虽然(“新”,但很多年)Java内存模型(JMM)并没有像旧代那样谈论主内存并且没有提供进度保证(实际上很难指定),执行volatile / 发生 - 在规范之前将导致从寄存器中逐出和线程之间的同步。