對於同步的規則定義

SEVEN clock發表於2020-11-29

執行緒間操作的定義

  1. 執行緒間操作指:一個程式執行的操作可被其他執行緒感知或被其他執行緒直接影響
  2. Java記憶體模型只描述執行緒間操作,不描述執行緒內操作,執行緒內操作按照執行緒內語義執行。

執行緒間操作有:

  • read操作(一般讀,即 非volatile讀)
  • write操作(一般寫,即 非volatile寫)
  • volatile read
  • volatile write
  • Lock.(鎖monitor)、Unlock
  • 執行緒的第一個和最後一個操作
  • 外部操作

所有執行緒間操作,都存在可見性問題,JMM需要對其進行規範

volatile關鍵字

可見性問題:讓一個執行緒對共享變數的修改,能夠及時的被其他執行緒看到。 Java記憶體模型規定:對volatile變數v的寫入,與所有其他執行緒後續對v的讀同步。

要滿足這些條件,所以volatile關鍵字就有這些功能:

  1. 禁止快取:

volatile變數的訪問控制符會加個

  • ACC_VOLATILE——https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5
  1. 對volatile變數相關的指令不做重排序。

double和long的特殊處理

由於《Java語言規範》的原因,對非volatile的double、long的單次寫操作是分兩次來進行的,每次操作其中32位,這可能導致第一次寫入後,讀取的值是髒資料,第二次寫完成後,才能讀到正確值。

在這裡插入圖片描述

  • 讀寫volatile修飾的long、double是原子性的。
  • 商業JVM不會存在這個問題,雖然規範沒要求實現原子性,但是考慮到實際應用,大部分都實現了原子性。
  • 《Java語言規範》中說道: 建議程式設計師將共享的64位值(long、double)用volatile修飾或正確同步其程式以避免可能的複雜情況。

對於同步的規則定義

  • volatile變數v的寫入,與所有其他執行緒後續對v的讀同步

  • 對於監視器m的解鎖與所有後續操作對於m的加鎖同步。
    在這裡插入圖片描述

  • 對於每個屬性寫入預設值(0,false,null)與每個執行緒對其進行的操作同步。
    在這裡插入圖片描述

  • 啟動執行緒的操作與執行緒中的第一個操作同步。
    在這裡插入圖片描述

  • 執行緒T2的最後操作與執行緒T1 發現執行緒T2已經結束同步。(isAlive,join可以判斷執行緒是否終結)
    在這裡插入圖片描述

  • 如果執行緒T1中斷了T2,那麼執行緒T1的中斷操作與其他所有執行緒發現T2被中斷了同步。通過丟擲InterruptedException異常,或者呼叫Thread.interruptedThread.isInterrupted
    在這裡插入圖片描述

Happens-before現行發生原則

happens-before 關係用於描述兩個有衝突的動作之間的順序,如果一個action happens before 另一個action,則第一個操作被第二個操作可見,JVM需要實現如下happens-before規則:

  • 某個執行緒中的每個動作都happens-before 該執行緒中該動作後面的動作。
  • 某個管程上的unlock 動作 happens-before 同一個管程上後續的 lock 動作。
  • 對某個 volatile 欄位的寫操作 happens-before 每個後續對該volatile欄位的讀操作。
  • 在某個執行緒物件上呼叫start()方法 happens-before被啟動執行緒中的任意動作。
  • 如果線上程t1中成功執行了t2.join(),則t2中的所有操作對t1可見。
  • 如果某個動作 A happens-before 動作 B,且B happens-before 動作C,則有 A happens-before C。

當程式中包含兩個沒有被 happens-before 關係排序的衝突訪問時,就稱存在資料競爭遵守了這個原則,也就意味著有些程式碼不能進行重排序,有些資料不能快取!

相關文章