挥发性密钥没有按预期工作

时间:2015-06-23 11:33:41

标签: java

我正在编写示例应用以了解易变行为。 据我所知,这个变量的值应该改为50,但我得到的输出是10

主要方法:

public class Volatile {
    public static void main(String[] args) {
        ShareObj so = new ShareObj();
        Thread1 t1 =  new Thread1(so);
        Thread2 t2 =  new Thread2(so);
        t1.start();
        t2.start();
        System.out.println(so.a);
    }
}

类:

class ShareObj {
    public volatile int a =10; 
}

class Thread1 extends Thread {
    ShareObj so;
    Thread1(ShareObj so) {
        this.so  = so;
    }
    public void run() {
        so.a += 10;
        System.out.println(so.a);
    }
}

class Thread2 extends Thread {
    ShareObj so;
    Thread2(ShareObj so) {
        this.so=so;
    }
    public void run() {
        so.a+=10;
        System.out.println(so.a);
    }
}

我期待的输出是50,但它是10

有什么建议吗?

3 个答案:

答案 0 :(得分:4)

首先,您需要等待两个线程完成,然后再打印结果。

...
t1.start();
t2.start();
t1.join(); // this will make main thread to wait untill thread is finished
t2.join();
.....

目前,您正在声明两个线程,但在它们中的任何一个线程可以更改volatile值之前,您的主线程是pring值并退出。

文档为here

答案 1 :(得分:3)

您有两个问题:

  1. 正如@immibis和@Beri已经指出的那样,你的System.out.println(so.a);方法中的main可能会在你的主题完成之前运行。

  2. 您在a处开始10并且有两个线程,每个线程按10递增,因此您应该30而不是50

  3. main方法更改为

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        ShareObj so = new ShareObj();
        Thread1 t1 = new Thread1(so);
        Thread2 t2 = new Thread2(so);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(so.a);
    }
    

    代码按预期打印30(作为最后一行;请注意,两个线程可能会输出20 30,所以你不应该' t依靠前两个输出。)

    我还想推荐@user3707125's answer,指出volatile可能仍然没有产生预期的结果(当你有更多的线程和/或更多的递增步骤时) - 参见还this Stack Overflow question

答案 2 :(得分:2)

我正在考虑您提供的代码是简化的,并且您使用调试执行以确保在System.out.println调用时两个线程都完成了他们的工作,否则您应该在打印之前使用Thread.join结果。

现在关于volatile关键字。 volatile表示此变量没有线程缓存。但是,这并不意味着具有此变量的操作将以原子方式执行。

代码so.a += 10如果简化只意味着so.a = so.a + 10,则此操作由JVM分两步执行(为简单起见,省略字段访问):

  1. 计算so.a + 10
  2. so.a分配给计算结果
  3. 现在让我们分析它可能如何影响执行(下一个案例发生的图像):

    1. thread_1:计算并放入堆栈(so.a + 10)=> 0 + 10 => 10(x)
    2. thread_2:计算并放入堆栈(so.a + 10)=> 0 + 10 => 10(y)
    3. thread_1:从stack =>中分配so.a so.a = x => so.a = 10
    4. thread_2:从stack =>中分配so.a so.a = y => so.a = 10
    5. main_thread:print so.a => print(10)
    6. 因此,即使使用volatile关键字,使用您编写的代码也是不安全的。

      如果要验证此案例,请将线程代码更改为:

      for (int i = 0; i < 1000000000; i++) {
          so.a += 1;
      }
      

      你会发现结果总是不同,几乎从不2kkk。