【问题标题】:How to use volatile correctly in Java如何在 Java 中正确使用 volatile
【发布时间】:2016-12-13 03:48:36
【问题描述】:

我正在研究Java Thread,当我分析以下由Example 8.3.1.4修改的代码时,关键字volatile让我感到困惑

public class Volatile {
    public static void main(String[] args){
        MyRunnable1 myRunnable1 = new MyRunnable1();
        MyRunnable2 myRunnable2 = new MyRunnable2();
        Thread t1 = new Thread(myRunnable1);
        Thread t2 = new Thread(myRunnable2);

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

class MyRunnable1 implements Runnable{
    public void run(){
        while(true){
            Test1.one();
        }
    }
}
class MyRunnable2 implements Runnable{
    public void run(){
        while(true){
            Test1.two();
        }
    }
}

class Test1{
    static volatile int i = 0;
    static volatile int j = 0;
    static void one(){
        i ++;
        j ++;
    }

    static void two(){
        System.out.println("i = " + i  + " j = " + j);
    }
}

一个输出段:

i = 60778110 j = 60778116
i = 60778402 j = 60778407
i = 60778630 j = 60778636
i = 60779062 j = 60779079
i = 60779492 j = 60779497
i = 60779784 j = 60779789
i = 60780161 j = 60780169
i = 60780625 j = 60780632
i = 60780936 j = 60780942

我的想法是因为volatilei ++ 发生在j ++ 之前,它们的初始值为零,修改后的值将立即刷新到主内存,所以任何时候i 线程@ 987654330@ 看到应该大于j。但是输出显示i 始终低于j

然后我修改two函数如下:

static void two(){
    System.out.println("j = " + j  + " i = " + i);
}

变化是ji之前输出,然后输出段如下:

j = 47324409 i = 47324412
j = 47324587 i = 47324593
j = 47324808 i = 47324813
j = 47324991 i = 47324996
j = 47325193 i = 47325196
j = 47325347 i = 47325353

j 总是低于i,这让我很吃惊。

我的想法是,j 较低,因为它首先连接,一段时间后 i 连接,在时间间隔内,one 函数执行导致 i 增加。

所以第一个连接的值会低于第二个连接的值,对吗?提前致谢!

【问题讨论】:

标签: java multithreading volatile


【解决方案1】:

你猜对了。不同之处在于 println 调用的参数是通过 3 个步骤构建的:

  1. 构建字符串 s = "i = " + i
  2. 构建字符串 s = s + " j = "
  3. 构建字符串 s = s + j

在 Runnable2 执行此操作时,尤其是在 step1 之后和最终打印之前,Runnable2 正忙于增加值。这会导致您看到的行为。

这不是volatile的问题。如果要 i 和 j 同步,则必须同步 Test1 类的方法。

【讨论】:

    【解决方案2】:

    实际上,volatile 只是阻止线程缓存值,而是强制执行“直写”和“直读”: 如果一个变量被一个线程缓存并被另一个线程更新,那么第一个线程的值可能永远不会改变,因为由于 Java 的缓存策略,它不需要刷新它的缓存。

    因此,当您有多个线程访问和更改原始资源(如 int、boolean 或引用类型引用值本身)时,您应该使用 volatile。

    这本身并不意味着 volatile 实际上使变量访问线程安全!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-11
      • 2012-01-19
      • 1970-01-01
      • 2013-04-23
      • 2018-09-25
      • 2017-02-19
      • 2015-05-20
      • 1970-01-01
      相关资源
      最近更新 更多