1 介紹
LockSupport類是Java6(JSR166-JUC)引入的一個類,提供了基本的線程同步原語。LockSupport提供的兩個主要方法就是park和unpark。
park譯為“停車”,官方文檔意為:許可。為了方便理解,在這里我們可以理解為阻塞,等待,掛起,而unpark我們理解為喚醒,恢復。
LockSupport同步線程和wait/notify不一樣,LockSupport並不需要獲取對象的監視器,而是給線程一個“許可”(permit)。而permit只能是0個或者1個。unpark會給線程一個permit,而且最多是1;而park會消耗一個permit並返回,如果線程沒有permit則會阻塞。
2 特點以及與wait/notify區別
park和unpark有以下特點
- permit不能疊加,也就是說permit的個數要么是0,要么是1。也就是不管連續調用多少次unpark,permit也是1個。線程調用一次park就會消耗掉permit,再一次調用park又會阻塞住。舉例如下
public class App {
public static void main(String[] args) throws Exception {
Thread boyThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("boy: 我要吃雞");
LockSupport.park();
System.out.println("boy: park1");
LockSupport.park(); // 第二次會阻塞住,因為只有一個permit
System.out.println("boy: park2");
System.out.println("boy: 開始吃雞了");
}
});
Thread girlThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("girl: 允許");
LockSupport.unpark(boyThread); // unpark兩次,但是permit不會疊加
LockSupport.unpark(boyThread);
}
});
boyThread.start();
girlThread.start();
}
運行結果是
boy: 我要吃雞
girl: 允許
boy: park1
-
unpark可以先於park調用。也就是我們在使用park和unpark的時候可以不用擔心park的時序問題造成死鎖。相比之下,wait/notify存在時序問題,wait必須在notify調用之前調用,否則雖然另一個線程調用了notify,但是由於在wait之前調用了,wait感知不到,就造成wait永遠在阻塞。我們可以考慮下之前的一個例子 為什么WAIT必須在同步塊中, 該例子中沒有使用同步塊產生死鎖的原因是由於錯誤的條件判斷,導致wait調用在notify之后,所以引起線程一直掛起。而unpark和park語義更加明確,它可以指明就是讓某一個線程阻塞/接觸阻塞。
-
park和unpark調用的時候不需要獲取同步鎖。我想原因應該也是和上述類似,我們可以先分析下wait/notify需要同步鎖的原因是因為兩個線程存在競態關系,必須保證在正確的條件下調用wait/notify,如果判斷錯誤了由於調用時序問題就會造成阻塞。而park/unpark不存在時序問題,所以線程可以繼續運行。
-
park的一般用法。park方法也有可能在沒有"任何理由"的情況下返回,所以通常要在一個循環中使用它並重新判斷可以返回的條件。這一點和Object.wait方法一樣,使用模式如下:
while (!canProceed()) { ... LockSupport.park(this); }}
當然如果沒有任何條件就park的話,也不需要while了
