在Java的synchronized
同步機制中,Monitor對象的EntryList和WaitSet是兩個關鍵隊列,它們分別管理不同狀態的線程。下面我將詳細解釋它們的工作原理,并提供代碼示例說明。
EntryList(鎖競爭隊列)
作用機制
EntryList保存了所有等待獲取鎖的線程。當鎖被某個線程持有時,其他嘗試獲取該鎖的線程會被放入EntryList中,處于BLOCKED狀態。
工作流程
- 線程嘗試獲取鎖時發現鎖已被持有
- 線程被放入EntryList等待
- 當持有鎖的線程釋放鎖時,JVM會從EntryList中選擇一個線程喚醒
- 被喚醒的線程嘗試獲取鎖
代碼示例
public class EntryListDemo {private static final Object lock = new Object();public static void main(String[] args) {// 線程1先獲取鎖new Thread(() -> {synchronized (lock) {System.out.println("線程1獲取鎖");try {Thread.sleep(3000); // 模擬長時間操作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("線程1釋放鎖");}}).start();// 線程2稍后嘗試獲取鎖new Thread(() -> {try {Thread.sleep(100); // 確保線程1先獲取鎖} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock) { // 這里會被放入EntryList等待System.out.println("線程2獲取鎖");}}).start();}
}
輸出結果:
線程1獲取鎖
(等待約3秒)
線程1釋放鎖
線程2獲取鎖
WaitSet(等待隊列)
作用機制
WaitSet保存了調用了wait()
方法的線程。這些線程已經獲得了鎖,但主動釋放鎖并等待被喚醒。
工作流程
- 線程獲得鎖后調用
wait()
方法 - 線程釋放鎖并進入WaitSet,處于WAITING狀態
- 其他線程調用
notify()
/notifyAll()
時,線程從WaitSet轉移到EntryList - 當再次獲得鎖時,從
wait()
調用處繼續執行
代碼示例
public class WaitSetDemo {private static final Object lock = new Object();private static volatile boolean condition = false;public static void main(String[] args) throws InterruptedException {// 等待線程Thread waitingThread = new Thread(() -> {synchronized (lock) {System.out.println("等待線程獲得鎖");while (!condition) {try {System.out.println("等待線程調用wait()");lock.wait(); // 釋放鎖并進入WaitSetSystem.out.println("等待線程被喚醒");} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("等待線程完成工作");}});// 通知線程Thread notifyingThread = new Thread(() -> {synchronized (lock) {System.out.println("通知線程獲得鎖");condition = true;lock.notify(); // 喚醒WaitSet中的一個線程System.out.println("通知線程已發送通知");}});waitingThread.start();Thread.sleep(500); // 確保等待線程先執行notifyingThread.start();}
}
輸出結果:
等待線程獲得鎖
等待線程調用wait()
通知線程獲得鎖
通知線程已發送通知
等待線程被喚醒
等待線程完成工作
EntryList和WaitSet的區別
特性 | EntryList | WaitSet |
---|---|---|
線程狀態 | BLOCKED | WAITING |
進入方式 | 競爭鎖失敗自動進入 | 主動調用wait()進入 |
喚醒方式 | 鎖釋放時自動喚醒 | 必須通過notify()/notifyAll() |
鎖的狀態 | 從未獲得過鎖 | 曾獲得鎖但主動釋放 |
轉移目標 | 獲得鎖后進入運行狀態 | 被喚醒后進入EntryList |
完整生命周期示例
public class MonitorLifecycleDemo {private static final Object lock = new Object();public static void main(String[] args) {// 線程1:演示wait()和notify()new Thread(() -> {synchronized (lock) {System.out.println("線程1獲得鎖");try {System.out.println("線程1調用wait()進入WaitSet");lock.wait(); // 進入WaitSetSystem.out.println("線程1被喚醒,重新獲得鎖");} catch (InterruptedException e) {e.printStackTrace();}System.out.println("線程1釋放鎖");}}).start();// 線程2:競爭鎖進入EntryListnew Thread(() -> {try {Thread.sleep(100); // 確保線程1先獲得鎖} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock) {System.out.println("線程2從EntryList中被選中獲得鎖");System.out.println("線程2調用notify()喚醒WaitSet中的線程");lock.notify(); // 喚醒線程1,線程1進入EntryListtry {Thread.sleep(1000); // 保持鎖一段時間} catch (InterruptedException e) {e.printStackTrace();}System.out.println("線程2釋放鎖");}}).start();// 線程3:展示EntryList中的競爭new Thread(() -> {try {Thread.sleep(200); // 確保線程2已獲得鎖} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock) {System.out.println("線程3獲得鎖");}}).start();}
}
輸出結果:
線程1獲得鎖
線程1調用wait()進入WaitSet
線程2從EntryList中被選中獲得鎖
線程2調用notify()喚醒WaitSet中的線程
(等待約1秒)
線程2釋放鎖
線程1被喚醒,重新獲得鎖
線程1釋放鎖
線程3獲得鎖
這個示例展示了:
- 線程1獲得鎖后主動wait()進入WaitSet
- 線程2從EntryList中獲得鎖并notify()喚醒線程1
- 線程2釋放鎖后,線程1從WaitSet轉移到EntryList并重新獲得鎖
- 最后線程3獲得鎖