第2章,Java併發進制的底層原理

第2章,Java併發機制的底層原理

深入理解Java併發之synchronized實現原理

Java代碼在編譯後會變成Java字節碼,字節碼被類加載到JVM裏,JVM執行字節碼,最終需要轉化爲彙編指令在CPU上執行,Java中所使用的併發機制依賴於JVM的實現和CPU的指令。

可見性、原子性、有序性、實現併發機制的三大特性。

1、volatile原理

volatile是輕量級的synchronized,它在多處理器開發中保證了共享變量的“可見性”。可見性是指一個線程修改共享變量的時候,另外一個線程可以看到修改後的值。

如果一個字段被聲明成volatile的,Java線程內存模型確保所有線程看到這個變量的值是一致的。

volatile不具備原子性,不足以保證線程安全。

使用場景:Java線程安全之volatile關鍵字
1、對變量的寫操作不依賴於當前值。
2、該變量沒有包含在具有其他變量的不變式中。

volatile比synchronized的使用成本低,因爲它不需要上線文的切換與調度。

volatile底層的cpu指令中有lock指令,lock前綴的指令在多核處理器下會引發兩件事情:
大前提:每個處理器對同一塊內存會有自己的緩存。
1、它可以保證將當前處理器緩存行中的數據寫回到系統內存。
2、這個寫回內存的操作,會使其他cpu裏緩存了該內存地址的數據失效。每個處理器通過嗅探在總線上傳播的數據來檢查自己緩存值是不是過期了,當處理器發現自己緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態。當處理器對這個數據進行修改操作的時候,會重新從系統內存中把數據讀到處理器緩存裏。

2、synchronized原理

Synchronized,jdk1.6經過各種優化後,已經沒有那麼重了,爲了減少獲得鎖與釋放鎖帶來的性能消耗,引入了輕量級鎖與偏向鎖。

鎖從低到高依次爲:無鎖,偏向鎖,輕量級鎖,重量級鎖。鎖只能升級不能降級,這種策略也是爲了保證獲得鎖與釋放鎖的效率。

synchronized實現同步的基礎,java中的每一個對象都可以作爲鎖。
1、普通的同步方法,鎖的是當前的實例對象。
2、靜態同步方法,鎖的是當前類的class對象。
3、同步方法塊,鎖的synchronized括號裏配置的對象。

JVM基於進入和退出Monitor對象來實現方法同步與代碼塊同步。

代碼塊是使用monitorenter(插入到同步代碼塊的開始位置)和monitorexit(插入到同步代碼塊的結束與異常位置)指令來實現的。

任何一個對象都有一個monitor與之關聯,當且一個monitor被持有後,它將處於鎖定狀態。線程執行到monitorenter指令時,將會嘗試獲取對象所對應的monitor的所有權,即嘗試獲得對象的鎖。

鎖存在於對象頭的mark word中。

synchronized用的鎖是存在Java對象頭裏的。

MarkWord裏默認存儲對象的HashCode,分代年齡,和鎖標記位。

以下的對象頭存儲的數據內容會隨着鎖標誌位的變化而變化。

3、Java中的鎖

Jdk1.6以後,引入了偏向鎖與輕量級鎖,鎖的狀態從低到高時無鎖,偏向鎖,輕量級鎖,重量級鎖。鎖只能升級不能降級,這是爲了提高獲得鎖和釋放鎖的效率。

偏向鎖

1、獲取:首先判斷要獲取的對象的對象頭中是否線程的ID,如果有,直接獲取偏向鎖,否則,先判斷對象頭中的鎖是否爲偏向鎖,如果是,用CAS來修改偏向鎖中的線程ID指向自己,否則根本不是偏向鎖的話,需要CAS來競爭鎖。
2、撤銷:等到競爭出現才釋放鎖,當其他線程嘗試競爭偏向鎖時,持有偏向鎖的線程纔會釋放鎖。需要等到全局安全點,纔可以撤銷偏向鎖,在全局安全點,沒有正在執行的字節碼。
3、關閉:通過命令關閉偏向鎖,進入輕量級鎖狀態。

輕量級鎖

1、加鎖:線程將對象頭中的markWord複製到自己的棧幀中,稱作DisplacedMarkWord,然後用CAS更新對象頭中的棧幀指向自己,失敗的話,就使用自旋來獲取鎖。
2、解鎖:線程使用DisplacedMarkWord,CAS替換對象頭,成功就解鎖,否則說明當前鎖存在競爭,鎖就膨脹爲重量級鎖。

鎖的優缺點比較

優點 缺點 適用場景
偏向鎖 執行時間快 競爭鎖時多了撤銷鎖的代價 適用一個線程訪問同步塊的場景
輕量級鎖 競爭的線程不會阻塞,提高了程序的響應速度 始終得不到鎖的線程會自旋獲取所鎖,消耗CPU 追求響應時間,同步塊執行時間很快
重量級鎖 線程競爭不會適用自旋,不消耗CPU 線程阻塞,響應時間慢 追求吞吐量,同步塊執行時間比較長

4、獲取Class對象的三種方式

1、class.forName方法:System.out.println(Class.forName(“chapter01.Dog”));
2、對象.getClass方法:System.out.println(dog1.getClass());
3、類.class變量:System.out.println(Dog.class);

5、CAS

CAS:compare and swap,比較並交換,傳遞進去一箇舊值,一個新值,當目標是舊值的時候,纔將目標更新成新值。

處理器實現原子操作
1、使用總線鎖保證原子性:處理器鎖住總線,其他處理器不可以操作共享內存。
2、使用緩存鎖保證原子性:只鎖住對應緩存中的內容,其他內存地址中的內容仍然可以訪問。

以下情況無法使用緩存鎖定,只能使用總線鎖定:
1、當操作的數據不能緩存在處理器中,或者是要操作的數據緩存在不止一個緩存行中。
2、某些處理器不支持緩存鎖定。

Java實現原子操作

1、自旋CAS操作:AtomicBoolean,AtomicInteger,AtomicLong,還有原子加1,減1等。CAS操作的三大問題:

  • 只能操作一個原子數據:把多個對象組合成一個來操作。
  • ABA問題:使用版本號來解決此問題。
  • 循環時間長,開銷大:如JVM支持處理器的pause命令,則可解決此問題。

2、鎖:JVM內部實現了很多的鎖機制,如偏向鎖,輕量級鎖,互斥鎖等,除了偏向鎖,其他的鎖內部也是使用了自旋CAS來獲取鎖和釋放鎖的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章