什麼是監視器(Monitor)?
在Java中,監視器(Monitor)是用來實現執行緒同步的一種機制。每個Java物件都有一個與之關聯的監視器,執行緒可以透過synchronized
關鍵字來獲取和釋放物件的監視器。監視器的主要作用是確保在同一時刻只有一個執行緒可以執行同步塊或同步方法,從而實現執行緒的互斥訪問。
監視器的組成部分
監視器通常包含以下三個關鍵部分:
- 入口集(Entry Set):等待獲取監視器鎖的執行緒集合。
- 所有者執行緒(Owner Thread):當前持有監視器鎖的執行緒。
- 等待集(Wait Set):呼叫了
wait()
方法並進入等待狀態的執行緒集合。
執行緒等待的地方
- 入口集(Entry Set):執行緒在嘗試進入同步塊或同步方法時,如果無法獲取監視器鎖,它們會進入入口集等待。這些執行緒處於阻塞狀態,等待獲取監視器鎖。
- 等待集(Wait Set):執行緒在呼叫
wait()
方法後,會釋放監視器鎖並進入等待集。這些執行緒處於等待狀態,直到被其他執行緒透過notify()
或notifyAll()
方法喚醒。
執行緒狀態轉換示意圖
以下是執行緒在不同狀態之間轉換的過程示意圖:
- 新建狀態(New):執行緒被建立,但尚未啟動。
- 可執行狀態(Runnable):執行緒已經啟動,可以執行但不一定正在執行。
- 阻塞狀態(Blocked):執行緒在入口集中,等待獲取監視器鎖。
- 等待狀態(Waiting):執行緒在等待集中,等待其他執行緒透過
notify()
或notifyAll()
喚醒。 - 超時等待狀態(Timed Waiting):執行緒在等待集中,等待特定時間後被喚醒。
- 終止狀態(Terminated):執行緒已經結束執行。
示例程式碼解釋
我們透過一個示例程式碼來解釋執行緒在不同狀態之間的轉換:
public class MonitorExample {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Thread 1: Acquired lock, entering wait state.");
lock.wait();
System.out.println("Thread 1: Woken up, reacquired lock.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 2: Acquired lock, notifying.");
lock.notify();
System.out.println("Thread 2: Notified, releasing lock.");
}
});
thread1.start();
try {
Thread.sleep(100); // Ensure thread1 starts first and enters wait state
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
thread2.start();
}
}
過程解釋
-
Thread 1 獲取監視器鎖:
Thread 1
進入同步塊並獲取監視器鎖。- 呼叫
lock.wait()
方法,Thread 1
釋放監視器鎖並進入等待集。
-
Thread 2 獲取監視器鎖:
Thread 2
進入同步塊並獲取監視器鎖(此時Thread 1
已在等待集)。- 呼叫
lock.notify()
方法,喚醒等待集中的一個執行緒(即Thread 1
)。
-
Thread 2 釋放監視器鎖:
Thread 2
退出同步塊,釋放監視器鎖。
-
Thread 1 重新獲取監視器鎖:
- 被喚醒的
Thread 1
從等待集中移動到鎖池,重新競爭獲取監視器鎖。 Thread 1
成功獲取監視器鎖後,從wait()
方法返回,繼續執行後續程式碼。
- 被喚醒的
總結
- 監視器(Monitor):用於實現執行緒同步,每個Java物件都有一個監視器。
- 入口集(Entry Set):執行緒在嘗試進入同步塊或同步方法時,如果無法獲取監視器鎖,會進入入口集等待。
- 等待集(Wait Set):執行緒在呼叫
wait()
方法後,會釋放監視器鎖並進入等待集,等待被喚醒。 - 狀態轉換:執行緒在不同狀態之間轉換,包括新建、可執行、阻塞、等待、超時等待和終止狀態。