我正在编写示例应用以了解易变行为。
据我所知,这个变量的值应该改为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
。
有什么建议吗?
答案 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)
您有两个问题:
正如@immibis和@Beri已经指出的那样,你的System.out.println(so.a);
方法中的main
可能会在你的主题完成之前运行。
您在a
处开始10
并且有两个线程,每个线程按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
(作为最后一行;请注意,两个线程可能会输出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分两步执行(为简单起见,省略字段访问):
so.a + 10
so.a
分配给计算结果现在让我们分析它可能如何影响执行(下一个案例发生的图像):
so.a
so.a = x
=> so.a = 10
so.a
so.a = y
=> so.a = 10
so.a
=> print(10)
因此,即使使用volatile
关键字,使用您编写的代码也是不安全的。
如果要验证此案例,请将线程代码更改为:
for (int i = 0; i < 1000000000; i++) {
so.a += 1;
}
你会发现结果总是不同,几乎从不2kkk。