java併發特性及其周邊

a_higher發表於2020-11-17

java執行緒安全的本質是執行緒執行結果具有正確性

執行緒三大特性(參考這篇

原子性

原子性就指是把一個操作或者多個操作視為一個整體,在執行的過程不能被中斷的特性叫原子性。

原因:我們執行一個操作時,可能會把操作拆分成多個指令交給CPU執行。IO、記憶體、CPU快取他們的操作速度有著巨大的差距,作業系統就有了程式和時間片的概念

可見性

我們在操作CPU快取過程中,由於多個CPU快取之間獨立不可見的特性,導致共享變數的操作結果無法預期

解決:為解決了快取不可見問題硬體程式就制定了一套保證快取之間可見的協議MESI(Modified Exclusive Share Invalid),MESI協議通過標識快取資料的狀態,來決定CPU何時把快取的資料寫入到記憶體,何時從快取讀取資料,何時從記憶體讀取資料。

MESI是一個不完美的解決方案,為了彌補MESI造成的效能問題,所以就有了本地快取(store bufferes),讓其能非同步mesi

但它又引入了其他的問題,因為把資料同步到記憶體中的過程是非同步的,資料儲存到記憶體中的時間點沒辦法保證;這個過程中當前CPU可以從store bufferes裡面讀取到最新的資料,但是其他快取就有可能讀取到的是之前的無效資料。所以在硬體上提供了處理方法---設定記憶體屏障。但是記憶體屏障的指令並非每個作業系統都是統一的,不同的作業系統的指令和效果可能各不相同,而且不同的快取方式也有可能不盡相同,所以為了讓寫程式的人只需要關心程式上的程式碼而不需要關心這麼底層的邏輯,我們的程式語言都會有一套統一管理這套記憶體可見性的規則,而JAVA用來管理這套記憶體可見性規則的模型就叫JMM

                                                                    Java記憶體模型與硬體記憶體架構的關係圖

 

                                                                                 Java執行緒和硬體處理機圖

有序性

編譯器優化可能會帶來的指令重排序問題

可見性有序性原子性

synchronized

可見性有序性

Volatile

final關鍵字

Happens-Before原則

原子性

atomic

附加知識

java物件結構(參考這篇

在HotSpot虛擬機器中,物件在記憶體中儲存的佈局可以分為3塊區域:物件頭(Header)(

  1. markword
    第一部分markword,用於儲存物件自身的執行時資料,如雜湊碼(HashCode)、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等,這部分資料的長度在32位和64位的虛擬機器(未開啟壓縮指標)中分別為32bit和64bit,官方稱它為“MarkWord”。
  2. klass
    物件頭的另外一部分是klass型別指標,即物件指向它的類後設資料的指標,虛擬機器通過這個指標來確定這個物件是哪個類的例項.
  3. 陣列長度(只有陣列物件有)
    如果物件是一個陣列, 那在物件頭中還必須有一塊資料用於記錄陣列長度.

)、例項資料(Instance Data)和對齊填充(Padding)(HotSpot VM的自動記憶體管理系統要求物件起始地址必須是8位元組的整數倍,換句話說,就是物件的大小必須是8位元組的整數倍。而物件頭部分正好是8位元組的倍數(1倍或者2倍),因此,當物件例項資料部分沒有對齊時,就需要通過對齊填充來補全。)

執行new A()的時候,JVM 做了什麼工作。首先,如果這個類沒有被載入過,JVM就會進行類的載入,並在JVM內部建立一個instanceKlass物件表示這個類的執行時後設資料(相當於Java層的Class物件)。初始化物件的時候(執行invokespecial A::),JVM就會建立一個instanceOopDesc物件表示這個物件的例項,然後進行Mark Word的填充,將後設資料指標指向Klass物件,並填充例項變數。

這裡寫圖片描述

執行new A()的時候,JVM 做了什麼工作。首先,如果這個類沒有被載入過,JVM就會進行類的載入,並在JVM內部建立一個instanceKlass物件表示這個類的執行時後設資料(相當於Java層的Class物件)。初始化物件的時候(執行invokespecial A::),JVM就會建立一個instanceOopDesc物件表示這個物件的例項,然後進行Mark Word的填充,將後設資料指標指向Klass物件,並填充例項變數。

成員變數重排序順序

  1. double (8位元組) 和 long (8位元組)
  2. int (4位元組) 和 float (4位元組)
  3. short (2位元組) 和 char (2位元組):char在java中是2個位元組。java採用unicode,2個位元組(16位)來表示一個字元。
  4. boolean (1位元組) 和 byte (1位元組)
  5. reference引用 (4/8 位元組)
  6. <子類欄位重複上述順序>

markword

狀態標誌位儲存內容
未鎖定01物件雜湊碼、物件分代年齡
輕量級鎖定00指向鎖記錄的指標
膨脹(重量級鎖定)10執行重量級鎖定的指標
GC標記11空(不需要記錄資訊)
可偏向01偏向執行緒ID、偏向時間戳、物件分代年齡

32位虛擬機器在不同狀態下markword結構如下圖所示:

這裡寫圖片描述

偽共享(參考http://ifeve.com/falsesharing/

cache line,cpu的快取系統中是以快取行(cache line)為單位儲存的,快取行是2的整數冪個連續位元組,一般為32-256個位元組。最常見的快取行大小是64個位元組,cache line是cache和memory之間資料傳輸的最小單元

Write-through(直寫模式)在資料更新時,同時寫入快取Cache和後端儲存。此模式的優點是操作簡單;缺點是因為資料修改需要同時寫入儲存,資料寫入速度較慢。

Write-back(回寫模式)在資料更新時只寫入快取Cache。只在資料被替換出快取時,被修改的快取資料才會被寫到後端儲存。此模式的優點是資料寫入速度快,因為不需要寫儲存;缺點是一旦更新後的資料未被寫入儲存時出現系統掉電的情況,資料將無法找回。

原因:

jdk1.8用@sun.misc.Contended解決

 

相關文章