如果熟悉Java并发编程的话,应该知道在多线程共享变量的情况下,存在内存可见性问题:
在一个线程中对某个变量进行赋值,在另外一个线程中读取该变量的值,读取到的可能仍然是以前的值;
这里并非说的是时序的问题,例如读取操作在赋值操作之前执行了,而是说,
即使在另外一个线程中循环读取该变量的值,也可能永远看不到变量的最新值,或者一段时间内看不到。
请看下面的代码片
1 public class Main extends Thread { 2 private static boolean flag = false; 3 4 @Override 5 public void run() { 6 while (!flag); 7 } 8 9 public static void main(String[] args) { 10 Main m = new Main(); 11 m.start(); 12 try { 13 Thread.sleep(200); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 flag = true; 18 try { 19 m.join(); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 System.out.println("done"); 24 } 25 }
这段代码在Windows,Linux,MacOS下的HotSpot中运行都不能结束
将变量flag声明为volatile的话,程序可正常结束,
类似的程序在Android的dalvik下运行并没有问题
奇怪的是,如果增加一个print语句,它也可以正常结束。
如果查看字节码的话,发现除了flag变量的volatile标记之外,字节码并无不同
从这里我们可以推断,并非是字节码执行的问题,因此怀疑是JIT的缘故,关掉JIT,再次运行,果然就都正常了。
如果是JIT的话,生成了机器代码,按照CPU的缓存一致性协议,变量有可能短时间之内不可见,那也应该过一段时间就能可见了,计算机上同时运行着这么多程序,像store buffer这样的结构应该会不时被刷新到混存中,甚至内存中,应该不至于导致永远不可见。
那么到底是什么导致了永久不可见?不是字节码执行引擎,不是硬件问题,唯一可能的就是JIT生成的代码导致的了。
那么接下来就是查看JIT代码了:
时间: 2024-11-08 00:12:40