個人博客請訪問 http://www.x0100.top
java.util.concurrent 中源碼頻繁使用的 LockSupport 來阻塞線程和喚醒線程,如 AQS 的底層實現用到 LockSupport.park()方法和 LockSupport.unpark()方法。
LockSupport 到底是什麼?同樣是阻塞和喚醒線程爲什麼不用 Object 的 wait()/notify 方法?
1. LockSupprot 方法介紹
LockSupport 提供 park()和 unpark()方法實現阻塞線程和解除線程阻塞。
阻塞線程:
-
void park():阻塞當前線程,如果調用 unpark 方法或者當前線程被中斷,才能從 park()方法中返回
-
void park(Object blocker):功能同方法 1,入參增加一個 Object 對象,用來記錄導致線程阻塞的阻塞對象,方便進行問題排查;
-
void parkNanos(long nanos):阻塞當前線程,最長不超過 nanos 納秒,增加了超時返回的特性;
-
void parkNanos(Object blocker, long nanos):功能同方法 3,入參增加一個 Object 對象,用來記錄導致線程阻塞的阻塞對象,方便進行問題排查;
-
void parkUntil(long deadline):阻塞當前線程,直到 deadline;
-
void parkUntil(Object blocker, long deadline):功能同方法 5,入參增加一個 Object 對象,用來記錄導致線程阻塞的阻塞對象,方便進行問題排查;
每個 park 方法都對應有一個帶有 Object 阻塞對象的重載方法。增加了一個 Object 對象作爲參數,此對象在線程受阻塞時被記錄,以允許監視工具和診斷工具確定線程受阻塞的原因。
喚醒線程:
void unpark(Thread thread):喚醒處於阻塞狀態的指定線程
使用舉例
public class LockSupportDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
LockSupport.park();
System.out.println("thread線程被喚醒");
});
thread.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.unpark(thread);
}
}
thread線程park阻塞。
主線程sleep3s之後,unpark(thread)。
thread被喚醒,輸出"thread線程被喚醒"。
2. 原理
每個線程都會與一個許可關聯,這個許可對應一個 Parker 的實例,Parker 有一個 int 類型的屬性_count。
park()方法:
-
將_count 變爲 0
-
如果原_count==0,將線程阻塞
unpark()方法:
-
將_count 變爲 1
-
如果原_count==0,將線程喚醒
3. 特殊之處
-
Object 的 wait()/notify 方法需要獲取到對象鎖之後在同步代碼塊裏才能調用,而 LockSupport 不需要獲取鎖。所以使用 LockSupport 線程間不需要維護一個共享的同步對象,從而實現了線程間的解耦。
-
unark()方法可提前 park()方法調用,所以不需要擔心線程間執行的先後順序。
-
多次調用 unpark()方法和調用一次 unpark()方法效果一樣,因爲 unpark 方法是直接將_counter 賦值爲 1,而不是加 1。
-
許可不可重入,也就是說只能調用一次 park()方法,如果多次調用 park()線程會一直阻塞。