Java中的公平鎖和非公平鎖

luorx發表於2024-11-22

公平鎖和非公平鎖是多執行緒程式設計中對鎖獲取策略的兩種不同實現,主要區別在於鎖對等待執行緒的排程方式。


公平鎖

公平鎖(Fair Lock)遵循 先來先服務 的原則,執行緒按照請求鎖的順序依次獲取鎖。

特點

  1. 排隊機制

    • 執行緒請求鎖時,如果鎖被佔用,會進入一個等待佇列。

    • 當鎖被釋放時,佇列中等待最久的執行緒優先獲得鎖。

  2. 避免執行緒飢餓

    • 公平鎖可以防止某些執行緒長時間無法獲取鎖的問題。

  3. 效能較低

    • 排隊和喚醒執行緒的開銷較高,可能導致整體效能下降。

實現方式

在 Java 的 ReentrantLock 中,透過將構造方法的引數 fair 設定為 true 來啟用公平鎖:

ReentrantLock lock = new ReentrantLock(true);

示例

import java.util.concurrent.locks.ReentrantLock;
​
public class FairLockExample {
    private static final ReentrantLock lock = new ReentrantLock(true);
​
    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 2; i++) {
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + " got the lock");
                } finally {
                    lock.unlock();
                }
            }
        };
​
        Thread t1 = new Thread(task, "Thread-1");
        Thread t2 = new Thread(task, "Thread-2");
        Thread t3 = new Thread(task, "Thread-3");
​
        t1.start();
        t2.start();
        t3.start();
    }
}

執行結果通常會按照執行緒的啟動順序依次獲取鎖,例如:

Thread-1 got the lock
Thread-2 got the lock
Thread-3 got the lock
...

非公平鎖

非公平鎖(Non-Fair Lock)是預設的鎖實現,執行緒嘗試直接競爭鎖,不關注排隊順序。

特點

  1. 競爭機制

    • 每個執行緒都可以直接嘗試獲取鎖,成功則進入臨界區,失敗則進入等待佇列。

  2. 效能較高

    • 由於不需要維護嚴格的排隊順序,執行緒排程和喚醒的開銷較小,吞吐量更高。

  3. 可能導致執行緒飢餓

    • 某些執行緒可能長時間無法獲取鎖,因為新來的執行緒可能會插隊成功。

實現方式

在 Java 的 ReentrantLock 中,透過將構造方法的引數 fair 設定為 false 或預設值啟用非公平鎖:

ReentrantLock lock = new ReentrantLock(false);

示例

import java.util.concurrent.locks.ReentrantLock;
​
public class NonFairLockExample {
    private static final ReentrantLock lock = new ReentrantLock();
​
    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 2; i++) {
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + " got the lock");
                } finally {
                    lock.unlock();
                }
            }
        };
​
        Thread t1 = new Thread(task, "Thread-1");
        Thread t2 = new Thread(task, "Thread-2");
        Thread t3 = new Thread(task, "Thread-3");
​
        t1.start();
        t2.start();
        t3.start();
    }
}

執行結果可能會出現執行緒搶佔的情況,例如:

Thread-1 got the lock
Thread-3 got the lock
Thread-2 got the lock
Thread-3 got the lock
...

公平鎖 vs 非公平鎖

特性公平鎖非公平鎖
鎖分配順序 按執行緒請求鎖的順序分配 隨機分配,允許插隊
執行緒飢餓 不會發生 可能發生
效能 效能較低,開銷較大 效能較高,吞吐量大
使用場景 需要嚴格控制執行緒公平性 更關注效能,允許一定的不公平性

選擇建議

  1. 使用公平鎖的場景

    • 需要避免執行緒飢餓,例如在多執行緒的資源分配中需要確保所有執行緒都能公平參與競爭。

  2. 使用非公平鎖的場景

    • 更注重效能,執行緒競爭較少時(如大部分操作鎖很快釋放),非公平鎖的效能優勢更明顯。

預設情況下,ReentrantLock 使用非公平鎖,因為在大多數場景下效能優先於嚴格的公平性。

相關文章