昨天听了下关于并发相关的分享,自己就下来写了小demo,想要测试一下,demo如下
import java.util.concurrent.TimeUnit; public class ThreadTest { public static void main(String[] args) throws InterruptedException { Test1 t = new Test1(); Thread th = new Thread(t); th.start(); TimeUnit.SECONDS.sleep(2); t.change(); System.out.println("has changed"); } } class Test1 implements Runnable { private /**volatile*/ boolean flag = true; int c = 0; @Override public void run() { while (flag) { int a = 1; int b = 1; c = a + b; } System.out.println("c: " + c); System.out.println("end"); } public void change() { this.flag = false; } }
很简答的一个demo,目的是想测试一下加volatile和不加volatile对线程间通讯的影响。感兴趣的小伙伴可以跑一下上面的demo(不加volatile的),看下结果。评论区大家可以留言,看看有没有不同的结果
华丽的分割线*
这里就不卖关子了,按理论来说,上面的demo不加volatile的情况,是会陷入死循环的,程序无法停止。加上volatile以后,由于解决了可见性问题,使得主线程可以读到修改后的flag值,从而使循环停止。
然鹅,神奇的事情发生了,不知道有没有小伙伴和我一样,运行上面的demo发现,诶?程序竟然自己停了....
当时我第一反应是代码写错了,但是让身边的小伙伴看了一下,并运行了一下,和我的结果竟然不一样!他的就没有停止.....
我甚至一度怀疑是我idea的问题(这以后有BUG不能怪我啊,idea出来背锅...)
当我冷静下来,仔细想了想,应该是坏境的问题,排查了一堆有的没的,又上网上查了些资料,最终将眼光放到了JVM上。
通过java -version看了下版本,有了新的发现:我的jvm竟然是32位的,用的竟然是默认的Client模式....又上网查了下JVM不同模式的差别,发现在对代码编译优化上存在差别。
看到这里为了验证一下,我将JVM的模式切换到了Serve进行验证。重新跑了下demo,果然,他停不下来了,疑惑解决。
其实发现是编译器优化导致的问题时,第一反应是前一段时间刚在《Effective java》这本书中提到的一个叫“提升”的优化。为了验证我看了下class反编译以后的文件,结果并没有看到想象中的优化结果......所以这个问题产生的真正原因,目前还没有定位到,后续有时间还有研究研究
感谢大家的时间,欢迎评论区留言写下自己看法,共同交流