多線程的可見性
一個線程對共享變量值的修改,能夠及時的被其他線程看到。
共享變量
如果一個變量在多個線程的工作記憶體中都存在副本,那麼這個變量就是這幾個線程的共享變量。
Java記憶體模型
JMM(Java Memory Model,簡稱JMM)描述了Java程式中各種變量(線程共享變量)的通路規則,以及在JVM中将變量存儲到記憶體和從記憶體中讀取出變量這樣的底層細節。它遵循四個原則:
- 所有的變量都存儲在主記憶體中
- 每個線程都有自己獨立的工作記憶體,裡面儲存該線程使用到的變量的副本(主記憶體中該變量的一份拷貝)
- 線程對共享變量的所有操作都必須在自己的工作記憶體中進行,不能直接從主記憶體中讀寫
- 不同線程之間無法直接通路其他線程工作記憶體中的變量,線程間變量的傳遞需要通過主記憶體來完成
共享變量可見性實作的原理
線程1對共享變量的修改要想被線程2及時看到,必須經過如下2個步驟:
- 把工作記憶體1中更新過的共享變量重新整理到主記憶體中
- 将主記憶體中最新的共享變量的值更新到工作記憶體2中
Java的記憶體模型:

Java語言層面支援的可見性實作方式有以下兩種:
- synchronized
- volatile
synchronized
JMM關于synchronized的規定:
- 線程解鎖前,必須把共享變量的最新值重新整理到主記憶體中
- 線程加鎖時,将清空工作記憶體中存儲的共享變量的值,進而使用共享變量時,必須從主記憶體中重新讀取最新的值。(注意:解鎖和加鎖,是指同一把鎖)
是以線程執行synchronized代碼執行互斥鎖的過程:
- 獲得互斥鎖。
- 清空工作記憶體。
- 從主記憶體拷貝變量的最新副本到工作記憶體。
- 執行代碼
- 将更改後的共享變量的值重新整理到主記憶體中
- 釋放互斥鎖
synchronize在JDK6之後,進行了很多優化。更多synchroinzed的講解,點選檢視:詳解Java多線程鎖之synchronized
volatile
不能保證原子性,但适合使用volatile修飾狀态标記量
通過加入記憶體屏障和禁止重排序優化來實作的
- 在每個volatile寫操作前插入StoreStore屏障,在寫操作後插入StoreLoad屏障
- 在每個volatile讀操作前插入LoadLoad屏障,在讀操作後插入LoadStore屏障
通俗地講:volatile變量在每次被線程通路時,都強迫從主記憶體中重讀該變量的值,而當該變量發生變化時,又會強迫将最新的值重新整理到主記憶體。這樣任何時刻,不同的線程總能看到該變量的最新值。
volatile寫操作:
volatile讀操作:
多線程的有序性
在Java記憶體模型中,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單線程程式的執行,卻會影響到多線程并發執行的正确性。
指令重排序:代碼書寫的順序與實際執行的順序不同,指令重排序是編譯器或者處理器為了提高程式性能而做的優化。
- 編譯器優化的重排序(編譯器優化)
- 指令集并行重排序(處理器優化)
- 記憶體系統的重排序(處理器優化)
happens-before原則
JMM可以通過happens-before關系向程式員提供跨線程的記憶體可見性保證(如果A線程的寫操作a與B線程的讀操作b之間存在happens-before關系,盡管a操作和b操作在不同的線程中執行,但JMM向程式員保證a操作将對b操作可見)。
- 程式次序規則:在一個線程内一段代碼的執行結果是有序的。就是還會指令重排,但是随便它怎麼排,結果是按照我們代碼的順序生成的不會變!
- 管程鎖定規則:就是無論是在單線程環境還是多線程環境,對于同一個鎖來說,一個線程對這個鎖解鎖之後,另一個線程擷取了這個鎖都能看到前一個線程的操作結果!(管程是一種通用的同步原語,synchronized就是管程的實作)
- volatile變量規則:就是如果一個線程先去寫一個volatile變量,然後一個線程去讀這個變量,那麼這個寫操作的結果一定對讀的這個線程可見。
- 傳遞規則:happens-before原則具有傳遞性,即A happens-before B , B happens-before C,那麼A happens-before C。
- 線程啟動規則:在主線程A執行過程中,啟動子線程B,那麼線程A在啟動子線程B之前對共享變量的修改結果對線程B可見。
- 線程終止規則:在主線程A執行過程中,子線程B終止,那麼線程B在終止之前對共享變量的修改結果線上程A中可見。
- 線程中斷規則:對線程interrupt()方法的調用先行發生于被中斷線程代碼檢測到中斷事件的發生,可以通過Thread.interrupted()檢測到是否發生中斷。
- 對象終結規則:一個對象的初始化的完成,也就是構造函數執行的結束一定 happens-before它的finalize()方法。