2015-06-23 25 views
0

正在编写示例应用程序以了解易失性行为。 根据我这个变量的值应改为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); 
    } 
} 

我期待的输出是5010

有什么建议吗?

+1

一方面,在'main'运行'System.out.println(so.a);'(因为启动一个线程比打印某个东西慢)'时,你的线程可能没有运行一个语句 – immibis

+4

为什么你期望50? – Eran

+0

注意在这个例子中,在实践中你可能会得到相同的结果或不具有不稳定性。 – immibis

回答

4

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

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

目前,你说明两个线程,但在此之前任何人可以改变挥发性价值,你的主线程是普林值和退出。

Docs是here

3

你有两个问题:

  1. 由于@immibis和@Beri已经在你main方法的线程都完成之前可能会运行所指出的,你System.out.println(so.a);

  2. 您在10开始a并有两个线程,其每个10增加它,所以你应该期望30而不是50

更改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预期(如最后一行后,请注意,这两个线程可能会输出2030,所以你不应该” t依赖于前两个输出)。

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

2

我考虑到你所提供的代码被简化了,你才能保证两个线程完成了他们的工作,当System.out.println有人呼吁使用调试执行,否则你应该打印结果之前使用Thread.join

现在关于volatile的关键字。 volatile意味着这个变量不会有线程缓存。然而,这并不意味着这个变量的操作将以原子方式执行。

如果简化的代码so.a += 10装置无非so.a = so.a + 10以上,在两个步骤中由JVM进行该操作时(允许省略为了简单起见,字段访问):

  1. 计算so.a + 10
  2. 分配so.a到计算结果

现在让我们来分析它是如何影响执行的(下一个案例发生的图像):

  1. thread_1:计算并把栈(so.a + 10)=> 0 + 10 => 10(x)的
  2. thread_2:计算并把栈(so.a + 10)=> 0 + 10 => 10(Y)
  3. thread_1:分配从堆栈so.a =>so.a = x =>so.a = 10
  4. thread_2:从栈分配so.a =>so.a = y =>so.a = 10
  5. main_thread:打印so.a =>print(10)

因此,使用您编写的代码即使使用volatile关键字也不安全。

如果你想验证这种情况下,线程的代码更改为类似:

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

而且你会看到的结果总是不同的,几乎从来没有2kkk。