Java volatile变量多线程行为

时间:2016-02-18 17:56:05

标签: java multithreading volatile

最近我接受了一次面试测试,并且对这个问题摸不着头脑。我会把我解释的内容和我想知道的是什么是正确的行为。我想确保我的理解是正确的,不是为了采访而是为了变得更好。

** Que:**我们在下面有一个计数器(java代码),如果说20个线程并行运行这个代码,那么a和b的值是否相同?如果b不是勇敢的怎么办

我的回答:可能会也可能不会。为什么 - 当存在竞争条件时,不能保证不同的线程将看到b或a的更新值,在这种情况下,值将是不同的。在这里,我不认为挥发性有任何区别。

我编写了一个简单的客户端,并在16核心笔记本电脑上运行此代码,线程数达到50,当尝试运行500次时,我可以看到a和b的相同值。但是,当我将线程数增加到200时,有时我会看到不同的值。

问题 - 对此有何正确答案,我的理解是否正确?为什么我的笔记本电脑上看到了不同的结果?

public class VolatileCounter implements Runnable {


    private static int a;
    private volatile static int b;

    public VolatileCounter() {
    }

    @Override
    public void run() {
        try{

            b++;
            a++;
        }finally {
                      //some system println....
        }

    }

    public void printNumbers() {
        System.out.println(b);
        System.out.println(a);
    }

}

3 个答案:

答案 0 :(得分:1)

即使有50个线程,该值也可能不同,所有这些都取决于竞争条件和上下文切换。由于a和b是原始类型,因此它是否是易失性无关紧要

答案 1 :(得分:1)

大多数问题是增量运算符由于采取了多个步骤而不是线程安全的:首先它们必须获取当前值,然后递增,然后使用新值更新字段,为其他线程提供机会交错操作和混淆结果。见Is the pre-increment operator thread-safe?。 (如果我向他们展示这样的话,这将是我希望听到我采访的人的主要观点。)

一些观察结果:

  • 仅仅因为并发问题可以发生并不意味着它必然会发生。提高并发性水平确实会给问题带来更多机会,这就是你所观察到的。

  • 这并不是说内存可见性不起作用,在本例中很难将其与非线程安全的操作符行为区分开来。如果没有volatile关键字,那么更新是否可见性是JVM实现的摆布。 PC上的JVM通常是相当宽容的。其他平台可能不是。

  • 如果在代码中包含printlns,则会在控制台上获得锁定,以便它们可以影响多线程行为和更新的可见性。

  • 有一种称为搭载的内存可见性技巧也可能起作用,影响您的类变量a的更新是否可见,请参阅:Volatile piggyback. Is this enough for visiblity?

答案 2 :(得分:1)

如果你开始这样的50个线程,启动线程需要很长时间,以至于它们很可能不会同时运行。如果它们确实碰巧同时运行,您可能会发现在任何一种情况下这些数字都略低于预期。

注意:使用volatile字段不会影响字段b,但它也影响b之后的所有内存访问,即它也影响{{1} }}

增量操作实际上是一个加载,增量和存储,因此实际上它不是线程安全的。