在 Java 里,公平鎖和非公平鎖是多線程編程中用于同步的兩種鎖機制,它們的主要差異在于獲取鎖的順序規則。下面是對二者的詳細介紹:
公平鎖
公平鎖遵循 “先來先服務” 原則,也就是線程獲取鎖的順序和請求鎖的順序一致。先請求鎖的線程會優先獲得鎖,這樣可以保證每個線程都有公平的機會獲取鎖,避免某個線程長時間等待。
不過,公平鎖在實現公平性時會增加額外的開銷,因為需要維護一個有序的等待隊列。當一個線程釋放鎖后,會從隊列頭部選取下一個線程來獲取鎖。
非公平鎖
非公平鎖不保證線程獲取鎖的順序和請求鎖的順序一致。當鎖被釋放時,任何等待的線程都有機會獲取鎖,而不考慮其請求的先后順序。
非公平鎖可能會讓某些線程先于等待時間長的線程獲取鎖,從而產生 “饑餓” 現象,即部分線程長時間得不到鎖。但非公平鎖的性能通常比公平鎖要好,因為它減少了線程上下文切換和等待隊列管理的開銷。
實現公平鎖和非公平鎖
在創建ReentrantLock時可以指定true或者false在指定公平或者非公平鎖(ReentrantLock和Synchronized關鍵字默認是非公平鎖),像下面這樣
// 創建公平鎖
Lock fairLock = new ReentrantLock(true);
// 創建非公平鎖
Lock unfairLock = new ReentrantLock(false);
如下是測試ReentrantLock實現公平鎖和非公平鎖的代碼
class Worker implements Runnable {private final Lock lock;private final String name;public Worker(Lock lock, String name) {this.lock = lock;this.name = name;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {lock.lock();try {System.out.println(name + " 獲得鎖,正在執行任務 " + i);Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();System.out.println(name + " 釋放鎖");}}}
}public class FairAndUnFair {public static void main(String[] args) {// 創建公平鎖Lock fairLock = new ReentrantLock(true);// 創建非公平鎖Lock unfairLock = new ReentrantLock(false);// 使用公平鎖System.out.println("使用公平鎖:");Thread t1 = new Thread(new Worker(fairLock, "線程1"));Thread t2 = new Thread(new Worker(fairLock, "線程2"));t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}// 使用非公平鎖System.out.println("\n使用非公平鎖:");t1 = new Thread(new Worker(unfairLock, "線程1"));t2 = new Thread(new Worker(unfairLock, "線程2"));t1.start();t2.start();}
}
在這個示例中,ReentrantLock
構造函數的參數true
表示創建公平鎖,false
表示創建非公平鎖。
適用場景
- 公平鎖:適用于對公平性要求較高的場景,如任務調度系統,需要保證每個任務都能按順序執行。
- 非公平鎖:適用于對性能要求較高,且對公平性要求較低的場景,如高并發的緩存系統。