二、執行緒安全性

weixin_33866037發表於2018-12-10

物件的狀態:儲存在狀態變數(例:例項/靜態域)中的資料。HashMap的狀態不僅儲存在物件本身,還儲存在許多Map.Entry物件

要物件執行緒安全,需用同步機制(synchronized、volatile變數、顯式鎖、原子變數)協同物件可變狀態的訪問。

如果當多個執行緒訪問同一個可變的狀態變數時,沒有使用合適的同步,那麼程式就會出現錯誤.有三種方式修復這個錯誤:

1. 線上程之間共享該狀態變數

2. 將狀態變數修改為不可變的變數

3. 在訪問狀態變數時,使用同步

ps:抽象和封裝會降低程式效能

2.1.執行緒安全類:

多個執行緒訪問時,不需額外的同步或協同,都能表現出正確的行為(行為與規範完全一致),那麼就稱這個類是執行緒安全.

無狀態的類: 不包含任何和其他類中域的引用.臨時狀態僅存在於區域性變數中

9930763-6a873fbdc4c78440.png
一個無狀態的Servlet

無狀態物件一定是執行緒安全的

2.2原子性

假定有兩個操作A和B,如果從執行A的執行緒來看,當另一個執行緒執行B時,要麼將B全部執行完,要麼完全不執行B.

9930763-a742178f02f7e876.png
執行緒不安全的統計方式

多個執行緒調service時,count值有偏差(實際:讀取、修改、寫入)

1) 競態條件:併發中,不恰當的執行時序,出現不正確的結果.(兩個人走岔路)

2) 複合操作

先檢查後執行; 讀取–修改–寫入等操作統稱為複合操作

當在無狀態的類中新增一個狀態時,如果該狀態完全由執行緒安全的物件來管理,這個類仍然是執行緒安全

9930763-982f72bed3c19f84.png

2.3.加鎖機制:

有了原子操作,為什麼還要採用加鎖機制?

9930763-1dcc033d0b3884bd.png

企圖通過原子引用來實現統計每一次的輸入,原子引用本身都是執行緒安全,UnsafeCachingFactorizer 中同樣存在競態條件.無法做到同時保證兩個值同時獲取和修改.

更新變數時,需對其他變數同時控制統一原子操作中

要保持狀態的一致性,就需要在單個原子操作中更新所有相關的狀態變數.

1) 內建鎖(支援原子性)synchronized,相當於互斥鎖

9930763-125dd0957a683a70.png

2) 重入

如果內建鎖不可重入,下面將發生死鎖

9930763-238a2223037db91b.png

獲取鎖的操作粒度是執行緒,而不是呼叫

JVM將記下鎖的持有者,計數器置為1,退出時,計數器遞減為0時,這個鎖將被釋放.

2.4.用鎖來保護狀態

不加區別的濫用synchronized,可能會導致程式中出現過多的同步,而且還並不足以保證複合操作都是原子的.

9930763-05ead297da14fb6d.png

contains和add都是原子方法,仍存在競態條件.

2.5.活躍性與效能:

不要盲目的為了效能而犧牲簡單性(可能會破壞安全性)

當執行時間較長的計算或者無法快速完成的操作時(比如: 網路IO,控制檯IO),一定不要持有鎖

保證安全下的效能優化:

9930763-7deab9f5b604f135.png

相關文章