LockSupport 工具類
LockSupport 是 rt.jar 包中的一個工具類,它的主要作用就是掛起
和喚醒
執行緒,並且該類是建立鎖和其它同步類的基礎。
LockSupport 類和每個使用它的執行緒都會關聯一個許可證
,在預設情況下呼叫 LockSupport 類的方法是不持有許可證的。LockSupport 是使用 Unsafe 類實現的。
下面介紹一下 LockSupport 中的主要方法。
void park()
如果在呼叫 park()方法之前已經獲得到關聯的許可證,則呼叫後會立即返回,否則會被禁止參與執行緒的排程,也就是被阻塞掛起。
如下程式碼,最終只會輸出“開始”,然後當前執行緒被掛起,因為在預設情況下呼叫執行緒是不持有許可證的。
public static void main(String[] args) throws Exception {
System.out.println("開始...");
LockSupport.park();
System.out.println("結束...");
}
//結果:
//開始...
在其它執行緒呼叫 unpark(Thread thread)方法並且將當前阻塞執行緒作為引數時,呼叫 park()方法阻塞的執行緒會被返回。當其它執行緒呼叫了阻塞執行緒的 interrupt()方法,設定中斷標誌或者執行緒被虛假喚醒,則阻塞執行緒也會被返回。
需要注意:
- 當呼叫 park()方法被阻塞的執行緒被其它執行緒中斷而返回時候,並不會丟擲 InterruptedException 異常。
- 許可證是一次性的。比如執行緒 B 連續呼叫了三次 unpark()方法,當執行緒 A 呼叫 park()方法就使用掉這個許可證,如果執行緒 A 再次呼叫 park()方法,則進入等待狀態。
void unpark(Thread t)
當一個執行緒呼叫 unpark()方法時,如果傳入的引數 thread 沒有持有許可證,則讓傳入的 thread 持有許可證;如果 thread 之前呼叫 park()後被掛起,則呼叫 unpark()時會喚醒該 thread;如果 thread 之前沒有呼叫 park(),則呼叫 unpark()方法後,再呼叫 park()方法時會立即返回。
public static void main(String[] args) throws Exception {
System.out.println("開始...");
LockSupport.unpark(Thread.currentThread());
LockSupport.park();
System.out.println("結束...");
}
//輸出結果:
//開始...
//結束...
看如下程式碼,加深對 park 和 unpark 的理解。
public static void main(String[] args) throws Exception {
Thread t = new Thread(() -> {
System.out.println("子執行緒開始...");
//掛起自己
LockSupport.park();
System.out.println("子執行緒結束...");
});
t.start();
Thread.sleep(1000);
System.out.println("主執行緒開始呼叫unpark");
LockSupport.unpark(t);
}
//輸出結果:
//子執行緒開始...
//主執行緒開始呼叫unpark
//子執行緒結束...
該段程式碼首先建立了子執行緒 t,子執行緒啟動後呼叫 park()方法。由於預設情況下子執行緒沒有持有許可證,則掛起自己。
主執行緒休眠 1 秒後,呼叫 unpark()並傳入子執行緒 t,這樣做就是為了讓子執行緒獲取到許可證,之後子執行緒中阻塞的 park()就會返回。
void parkNanos(long nanos)
和 park()方法類似,如果已經獲取到了許可證則呼叫該方法後會馬上返回。不同的是,如果沒有拿到許可證,則呼叫執行緒會被掛起 nanos 時間後自動返回。
另外 park()方法還支援帶有 blocker 引數的方法 void park(Object blocker)方法,當執行緒在沒有持有許可證的情況下呼叫 park()方法而被阻塞掛起時,這個 blocker 物件會被記錄到該執行緒內部。一般用於用於標識阻塞物件,該物件主要用於問題排查和系統監控。
void parkNanos(Object blocker,long nanos) 該方法相對於上個方法,增加了超時時間。
void parkUntil(Object blocker,long deadline)
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
引數 deadline 的時間單位為 ms,這個方法和 parkNanos(Object blocker,long nanos)的區別是,後者是從當前時間等待 nanos 秒時間,而前者是指定一個時間點,比如 2022-01-19 12:00:00。
總結
與 Object 類的 wait/notify 機制相比,park/unpark 有兩個優點:
- 以 thread 為操作物件更符合阻塞執行緒的直觀定義
- 操作更精準,可以準確地喚醒某一個執行緒(notify 隨機喚醒一個執行緒,notifyAll 喚醒所有等待的執行緒),增加了靈活性