關於Java變量的可見性問題

關於Java變量的可見性問題

博文前提

最近在oschina問答板塊看到了一個關於java變量在工做內存和主存中的可見性問題:synchorized,sleep 也能達到volatile 線程可見性的目的?,大體的問題描述以下:java

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 會幹什麼呢?ide

涉及知識解釋

  • volatile:此關鍵字保證了變量在線程的可見性,全部線程訪問由volatile修飾的變量,都必須從主存中讀取後操做,並在工做內存修改後當即寫回主存,保證了其餘線程的可見性,一樣效果的關鍵字還有final。學習

  • synchronized:全部同步操做都必須保證 一、原子性 二、可見性,因此在同步塊中發生的變化會立馬寫回主存優化

  • sleep:此方法只會讓出CPU執行時間,並不會釋放鎖。this

問題分析

Q1:爲何註釋代碼後程序不會終止?spa

A1:由於 boolean is=true 的變量值被前面線程(簡稱線程A)加載到本身的工做內存,在後面的線程(簡稱線程B)改變 boolean is=false 以後不必定會立馬寫入主存(不過這道題中應該會立刻寫入主存,由於線程執行完 is=false以後線程就要退出了),即使立馬寫入了主存後線程A也不必定立刻load到工做內存中,因此程序一直不會終止?這個是咱們大多數人想到的,但其實JVM針對如今的硬件水平已經作了很大程度的優化,基本上很大程度的保障了工做內存和主內存的及時同步,至關於默認使用了volatile。但只是最大程度!在CPU資源一直被佔用的時候,工做內存與主內存中間的同步,也就是變量的可見性就會不那麼及時!後面會驗證結論。.net

Q2:爲何取消註釋中的任何一個代碼塊(1,2,3),程序纔會終止?線程

A2:行號爲一、2的代碼有一個共同特色,就是都涉及到了synchronized 同步鎖,那麼是否像提問做者猜測的那樣synchronized會強制刷新主內存的變量值到線程棧?以及sleep方法也會刷新主存的變量值到線程棧呢?,事實上咱們前面說了synchronized只會保證在同步塊中的變量的可見性,而is變量並不在該同步塊中,因此顯然不是這個致使的。接下來咱們在代碼i++;後面加上如下代碼:code

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,但不釋放鎖!orm

結束

技術在於不斷的學習和成長,堅持寫博客 和技術輸出,對本身的成長會有很大幫助,若有錯誤 歡迎指正,拒絕人身攻擊。

相關文章
相關標籤/搜索