我們先來看段代碼:
1 class ThreadVolatileDemo extends Thread{ 2 static boolean flag=true;//注意該變量沒有被volatile修飾 3 @Override 4 public void run() { 5 while(flag){ 6 System.out.println("子線程"); 7 } 8 System.out.println("子線程結束"); 9 } 10 } 11 public class ThreadVolatile { 12 public static void main(String[] args) { 13 ThreadVolatileDemo tVD=new ThreadVolatileDemo(); 14 tVD.start(); 15 try { 16 Thread.sleep(1000); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 System.out.println("一秒鍾一結束"); 21 ThreadVolatileDemo.flag=false;//flag變量在一秒后在主線程中被修改為false 22 } 23 }
通過看代碼我們可以知道這是一個簡單的多線程代碼,子線程的run方法也很簡單,就是一個單純的while循環,我們先思考一下這段代碼可能的運行結果,看代碼可知,flag是一個普通變量,初始值為true,且沒有被volatile修飾,也就是說它不具備內存可見性,又因為主線程中修改flag變量是在一秒之后的,然而這時候子線程已經開啟了,且已經擁有了自己的本地內存,里面也已經存儲了flag變量的副本,因為沒有被volatile修飾,不具有可見性,所以子線程就不再和主內存中該變量值有任何關系,而是直接操作在本地內存上的變量值。因此由於子線程開啟后flag變量的副本值一直為true,所以子線程就一直陷入在while死循環中出不來。
但是!!! 當我運行完之后卻發現,代碼運行的結果跟我預想的有很大的差別,下面是代碼的真正運行結果。
通過結果可知,代碼並沒有陷入到循環中,這是為什么呢???
於是我稍稍改動了下子線程run方法中的代碼,如下所示,改完之后我發現代碼的運行結果跟我之前分析的結果卻是一樣的,代碼陷入了死循環中,這樣看來問題是出在了 【System.out.println("子線程");】這句上。難道說這條打印語句已經影響到了內存可見性嗎?
1 class ThreadVolatileDemo extends Thread{ 2 static boolean flag=true;//注意該變量沒有被volatile修飾 3 @Override 4 public void run() { 5 while(flag){ 6 //System.out.println("子線程"); 7 //屏蔽掉while循環中的打印語句 8 } 9 System.out.println("子線程結束"); 10 } 11 }
改完之后代碼的運行結果:
通過查看println源碼,可以發現println語句中有一個上鎖的操作:
通過查資料發現,在使用了synchronized上鎖這個操作后線程會做以下操作:
1.獲得同步鎖
2.清空工作內存
3.從主內存中拷貝對象副本到本地內存
4.執行代碼(打印語句或加加操作)
5.刷新主內存數據
6.釋放同步鎖
這也就是System.out.println()為何會影響內存可見性的原因了。