AtomicBoolean
主要用來解決併發程式設計中的執行緒安全問題,防止某段程式碼重複執行或確保某項任務只能執行一次。程式碼中常用來作為一個標誌變數,以控制併發流程。AtomicBoolean體現的是一種無鎖機制,依靠底層的高效的CAS原子操作實現,提供高效的執行緒安全操作。
CAS簡介
CAS的核心思想是'比較和交換'
如果記憶體中的值與預期值相等,則將其更新為新值,否則什麼都不操作。
三個運算元:記憶體值(V,被操作的變數存在記憶體中)預期值(E,操作前執行緒認為的值應該是此值)更新值(U,如果記憶體值與預期值相等則更新為此值)
比較V&E--> 如果V== E 把V的值更新為U,並返回true;--> 如果V!=E 則不更新V值,並返回false
底層是依賴CPU的硬體指令來實現。體現的是一種無鎖機制,不需要透過顯式的加鎖解鎖來控制執行緒的執行,避免了執行緒上下文切換帶來的開銷。
CAS的一些缺點 如ABA問題 可自行查閱。
AtomicBoolean中的幾個常用API
- get():獲取當前值
- set(boolean newValue):設定新值(非原子性)
- compareAndSet(boolean expect, boolean update):如果當前值(V)等於預期值(E)則將其更新為更新值(U)並返回true
- getAndSet(boolean newValue):原子性設定新值並返回舊值
AtomicBoolean中的CAS
核心方法:compareAndSet(boolean expect, boolean update)
ReentrantLock
ReentrantLock是一種基於AQS框架實現的應用,是JDK中實現併發程式設計的手段,它的功能類似synchronized是一種互斥鎖,可以保證執行緒安全。
- 可重入:同一個執行緒可以多次獲取同一個鎖而不會導致死鎖
- 公平性:可以根據執行緒請求鎖的順序分配(公平鎖)
- 效能上:ReentrantLock更適合複雜的併發場景
ReentrantLock鎖會導致整體的吞吐量下降,佇列中除了第一個執行緒 其他的執行緒都會阻塞(可能會導致其他執行緒餓死),cpu喚醒其他執行緒也會帶來開銷。
注意事項:
- 確保加鎖與解鎖配對(解鎖放在finally中確保發生異常,也能正常解鎖,避免鎖一直被佔用)
- 避免出現死鎖:多個執行緒嘗試獲取多把鎖時避免出現死鎖問題
- 鎖的順序要一致:確保不同執行緒獲取鎖的順序相同
- 嘗試獲取鎖(tryLock)時 要設定等待時間,避免長時間等待
- 不要長時間佔有鎖(將鎖的作用返回設定到最小化,減少臨界區的程式碼量)
- 正確處理中斷
- ReentrantLock支援可中斷鎖,如果執行緒在等待鎖時被中斷,會丟擲
InterruptedException
要能夠正確處理
- ReentrantLock支援可中斷鎖,如果執行緒在等待鎖時被中斷,會丟擲
將以上兩種方法混合使用(實際上應該是多餘的)整體程式碼結構如下
private ReentrantLock createXXXLock = new ReentrantLock();
private AtomicBoolean createXXXFlag = new AtomicBoolean(false);
if (createXXXFlag.get()) {
log.info("XXX");
return;
}
createXXXLock.lock();
try {
if (createXXXFlag.compareAndSet(false, true)) {
try {
// 業務邏輯處理
throw new RuntimeException("模擬異常");
} catch (Exception e) {
log.error("xxx", e.getMessage());
} finally {
// 無論邏輯是否成功,確保標誌位被重置
createXXXFlag.set(false);
}
} else {
log.info("xxxx");
}
} finally {
// 確保鎖的釋放
createXXXLock.unlock();
}
程式碼中看到有同事這樣使用,故學習一下~~~ 不喜勿噴