AtomicBoolean與ReentrantLock

有点儿意思發表於2024-12-03

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要能夠正確處理

將以上兩種方法混合使用(實際上應該是多餘的)整體程式碼結構如下

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();
}

程式碼中看到有同事這樣使用,故學習一下~~~ 不喜勿噴

相關文章