關於Java變數的可見度問題

來源:互聯網
上載者:User

標籤:預設   vat   syntax   try   載入   vol   寫部落格   做了   tab   

轉自:http://www.importnew.com/19434.html

博文前提

最近在oschina問答板塊看到了一個關於java變數在工作記憶體和主存中的可見度問題:synchorized,sleep 也能達到volatile 線程可見度的目的?,大致的問題描述如下:

123456789101112131415161718192021222324252627282930313233343536373839 package com.test;import java.util.concurrent.TimeUnit; public class test1 {     private static boolean is = true;    public static void main(String[] args) {        new Thread(new Runnable() {            @Override            public void run() {                int i = 0;                while(test1.is){                    i++;                    1 //synchronized (this) { } 會強制重新整理主記憶體的變數值到線程棧?                   2 //System.out.println("1"); println 是synchronized 的,會強制重新整理主記憶體的變數值到線程棧?                   3 //sleep 會從新load主記憶體的值?                      //    try {                     //       TimeUnit.MICROSECONDS.sleep(1);                     //   }catch (InterruptedException e) {                     //      e.printStackTrace();                      //   }                            }        }).start();         try {            TimeUnit.SECONDS.sleep(1);            catch (InterruptedException e) {                e.printStackTrace();              }        new Thread(new Runnable() {            @Override            public void run() {                is = false;  //設定is為false,使上面的線程結束while迴圈            }        }).start();    }}

問: 為什麼整個程式不會終止? 為什麼取消注釋中的任何一個代碼塊(1,2,3),程式才會終止?synchronized 會強制重新整理住記憶體的變數值到線程棧? sleep 會幹什麼呢?

涉及知識解釋
  • volatile:此關鍵字保證了變數線上程的可見度,所有線程訪問由volatile修飾的變數,都必須從主存中讀取後操作,並在工作記憶體修改後立即寫回主存,保證了其他線程的可見度,同樣效果的關鍵字還有final。
  • synchronized:所有同步操作都必須保證 1、原子性 2、可見度,所以在同步塊中發生的變化會立馬寫回主存
  • sleep:此方法只會讓出CPU執行時間,並不會釋放鎖。
問題分析

Q1:為什麼注釋代碼後程式不會終止?

A1:因為 boolean is=true 的變數值被前麵線程(簡稱線程A)載入到自己的工作記憶體,在後面的線程(簡稱線程B)改變 boolean is=false 之後不一定會立馬寫入主存(不過這道題中應該會馬上寫入主存,因為線程執行完 is=false之後線程就要退出了),即便立馬寫入了主存後線程A也不一定馬上load到工作記憶體中,所以程式一直不會終止?這個是我們大多數人想到的,但其實JVM針對現在的硬體水平已經做了很大程度的最佳化,基本上很大程度的保障了工作記憶體和主記憶體的及時同步,相當於預設使用了volatile。但只是最大程度!在CPU資源一直被佔用的時候,工作記憶體與主記憶體中間的同步,也就是變數的可見度就會不那麼及時!後面會驗證結論。

Q2:為什麼取消注釋中的任何一個代碼塊(1,2,3),程式才會終止?

A2:行號為1、2的代碼有一個共同特點,就是都涉及到了synchronized 同步鎖,那麼是否像提問作者猜想的那樣synchronized會強制重新整理主記憶體的變數值到線程棧?以及sleep方法也會重新整理主存的變數值到線程棧呢?,事實上我們前面說了synchronized只會保證在同步塊中的變數的可見度,而is變數並不在該同步塊中,所以顯然不是這個導致的。接下來我們在代碼i++;後面加上以下代碼:

123 for(int k=0;k<100000;k++){    new Object();}

再Run,程式立刻終止!為什嗎?在上面的 A1 中我們已經說了即便有JVM的最佳化,但當CPU一直被佔用的時候,資料的可見度得不到很好的保證,就像上面的程式一直迴圈做i++;運算佔用CPU,而為什麼加上上面的代碼後程式就會停止呢?因為對於大量new Object()操作來說,CPU已經不是主要佔時間的操作,真正的耗時應該在記憶體的分配上(因為CPU的處理速度明顯快過記憶體,不然也不會有CPU的寄存器了),所以CPU空閑後會遵循JVM最佳化基準,儘可能快的保證資料的可見度,從而從主存同步is變數到工作記憶體,最終導致程式結束,這也是為什麼sleep()方法雖然沒有涉及同步操作,但是依然可以使程式終止,因為sleep()方法會釋放CPU,但不釋放鎖!

結束

技術在於不斷的學習和成長,堅持寫部落格 和技術輸出,對自己的成長會有很大協助,如有錯誤 歡迎指正,拒絕人身攻擊。

關於Java變數的可見度問題

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.