標籤:contains 同步機制 包含 重入 引用 使用 特性 同步代碼塊 計算
一個對象是否需要是安全執行緒的取決於它是否被多個線程訪問。
當多個線程訪問同一個可變狀態量時如果沒有使用正確的同步規則,就有可能出錯。解決辦法:
- 不線上程之間共用該變數
- 將狀態變數修改為不可變的
- 在訪問狀態變數時使用同步機制
完全由安全執行緒類構造的程式也不一定是安全執行緒的,安全執行緒類中也可以包含非安全執行緒的類
一、什麼是執行緒安全性
安全執行緒是指多個線程在訪問一個類時,如果不需要額外的同步,這個類的行為仍然是正確的。(因為安全執行緒類中封裝了必要的同步代碼)
一個無狀態的類是安全執行緒的。無狀態類是指不包含任何域或也沒有引用其它類的域。一次特定計算的瞬間狀態,會唯一存在本地變數中。
二、原子性
1、競爭條件:由於不恰當的執行時許而出現了不正確的運行結果,當計算的正確性取決於交叉執行的順序時
錯誤原因:基於一個可能失效的觀察結果進行下一步操作
例1:讀取-修改-寫入 操作: 當兩個線程交叉執行並同時讀取到了相同的值,addOne操作便造成了偏差-1的結果。
1 public class Test {2 private int x = 0;3 public void addOne(){4 x++;5 }6 }
例2:先檢測後執行 操作:沒有進行同步操作的單件模式
2、複合操作
概念:包含一組必須以原子方式執行的操作以保證線程的安全性
原子操作是安全執行緒的 vs 競爭條件是不安全的
在無狀態的類中加入一個確保安全執行緒的狀態可以保證該類仍未安全執行緒的,但加入多個安全執行緒狀態時沒有辦法保證。
三、加鎖機制
1、內建鎖:java提供了強制原子性的內建鎖機制:synchronized 塊,每個java對象都可作為鎖,稱為內建鎖或監視鎖
- synchronized(lock){訪問或修改共用變數}
- synchronized方法為保證整個方法體為原子的,lock為this
- 靜態synchronized方法以Class對象為鎖
最多隻有一個對象持有該鎖,進入同步代碼塊獲得鎖,出代碼塊釋放鎖
synchronized特性不能被繼承,即覆蓋方法需要也寫synchronized關鍵字
2、重入
內建鎖是可以重入的,即某個線程可以獲得它已經持有的鎖。(例同步子類方法調用父類方法super.method()會請求它已經有的鎖)
JVM提供鎖內執行緒計數器予以保證正確釋放鎖
四、用鎖來保護對象
鎖使受保護的代碼以串列形式執行。
對於可能被多個線程訪問的狀態變數,訪問它的時候都要加鎖,不只是在寫入時加鎖。
每一個共用的或可變的狀態都應該由一個鎖保護
鎖機制僅僅阻止了其他線程獲得同一個鎖,而不能防止訪問對象
即使每個方法都使用了同步機制,但由他們呢構成的複合操作不一定保證並發正確。
例
1 public add(E e){2 if(!vector.contains(e)) 3 vector.add(e); 4 }
五、活躍性與效能
如果對整個services方法加鎖,方法內有某個計算耗時較長時,那麼將嚴重影響效率
修改代碼後將綠色箭頭指的大計算量部分重同步代碼中取出,可以並行作業,改進效能
java並發編程實戰:第二章----執行緒安全性