Java中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         }
 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()為何會影響內存可見性的原因了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM