LockSupport 阻塞原語

LockSupport是用來建立鎖和其餘同步類的基本線程阻塞原語。 
LockSupport中的park() 和 unpark() 的做用分別是阻塞線程和解除阻塞線程,並且park()和unpark()不會遇到「Thread.suspend 和 Thread.resume所可能引起的死鎖」問題。
由於park() 和 unpark()有許可的存在;調用 park() 的線程和另外一個試圖將其 unpark() 的線程之間的競爭將保持活性。java

concurrent包是基於AQS (AbstractQueuedSynchronizer)框架,AQS框架藉助於兩個類:Unsafe(提供CAS操做)和LockSupport(提供park/unpark操做)。所以,LockSupport可謂構建concurrent包的基礎之一。框架

兩個重點

  • 操做對象

歸根結底,LockSupport調用的Unsafe中的native代碼: 函數

public native void unpark(Thread jthread); 
public native void park(boolean isAbsolute, long time); 

兩個函數聲明清楚地說明了操做對象:park函數是將當前Thread阻塞,而unpark函數則是將另外一個Thread喚醒。this

與Object類的wait/notify機制相比,park/unpark有兩個優勢:1. 以thread爲操做對象更符合阻塞線程的直觀定義;2. 操做更精準,能夠準確地喚醒某一個線程(notify隨機喚醒一個線程,notifyAll喚醒全部等待的線程),增長了靈活性。spa

  • 關於許可

在上面的文字中,我使用了阻塞和喚醒,是爲了和wait/notify作對比。其實park/unpark的設計原理核心是「許可」。park是等待一個許可。unpark是爲某線程提供一個許可。若是某線程A調用park,那麼除非另一個線程調用unpark(A)給A一個許可,不然線程A將阻塞在park操做上。線程

有一點比較難理解的,是unpark操做能夠再park操做以前。也就是說,先提供許可。當某線程調用park時,已經有許可了,它就消費這個許可,而後能夠繼續運行。這實際上是必須的。考慮最簡單的生產者(Producer)消費者(Consumer)模型:Consumer須要消費一個資源,因而調用park操做等待;Producer則生產資源,而後調用unpark給予Consumer使用的許可。很是有可能的一種狀況是,Producer先生產,這時候Consumer可能尚未構造好(好比線程還沒啓動,或者還沒切換到該線程)。那麼等Consumer準備好要消費時,顯然這時候資源已經生產好了,能夠直接用,那麼park操做固然能夠直接運行下去。若是沒有這個語義,那將很是難以操做。設計

 

 1 import java.util.concurrent.locks.LockSupport;
 2 
 3 public class LockSupportTest1 {
 4 
 5     private static Thread mainThread;
 6 
 7     public static void main(String[] args) {
 8 
 9         ThreadA ta = new ThreadA("ta");
10         // 獲取主線程
11         mainThread = Thread.currentThread();
12 
13         System.out.println(Thread.currentThread().getName()+" start ta");
14         ta.start();
15 
16         System.out.println(Thread.currentThread().getName()+" block");
17         // 主線程阻塞
18         LockSupport.park(mainThread);
19 
20         System.out.println(Thread.currentThread().getName()+" continue");
21     }
22 
23     static class ThreadA extends Thread{
24 
25         public ThreadA(String name) {
26             super(name);
27         }
28 
29         public void run() {
30             System.out.println(Thread.currentThread().getName()+" wakup others");
31             // 喚醒「主線程」
32             LockSupport.unpark(mainThread);
33         }
34     }
35 }

運行結果code

main start ta
main block
ta wakup others
main continue

說明:park和wait的區別。wait讓線程阻塞前,必須經過synchronized獲取同步鎖。對象

 

 1 public class WaitTest1 {
 2 
 3     public static void main(String[] args) {
 4 
 5         ThreadA ta = new ThreadA("ta");
 6 
 7         synchronized(ta) { // 經過synchronized(ta)獲取「對象ta的同步鎖」
 8             try {
 9                 System.out.println(Thread.currentThread().getName()+" start ta");
10                 ta.start();
11 
12                 System.out.println(Thread.currentThread().getName()+" block");
13                 // 主線程等待
14                 ta.wait();
15 
16                 System.out.println(Thread.currentThread().getName()+" continue");
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20         }
21     }
22 
23     static class ThreadA extends Thread{
24 
25         public ThreadA(String name) {
26             super(name);
27         }
28 
29         public void run() {
30             synchronized (this) { // 經過synchronized(this)獲取「當前對象的同步鎖」
31                 System.out.println(Thread.currentThread().getName()+" wakup others");
32                 notify();    // 喚醒「當前對象上的等待線程」
33             }
34         }
35     }
36 }
相關文章
相關標籤/搜索