目錄java
在java併發包下各類同步組件的底層實現中,LockSupport的身影到處可見。JDK中的定義爲用來建立鎖和其餘同步類的線程阻塞原語。併發
*Basic thread blocking primitives for creating locks and other *synchronization classes.
咱們可使用它來阻塞和喚醒線程,功能和wait,notify有些類似,可是LockSupport比起wait,notify功能更強大,也好用的多。ide
public class WaitNotifyTest { private static Object obj = new Object(); public static void main(String[] args) { new Thread(new WaitThread()).start(); new Thread(new NotifyThread()).start(); } static class WaitThread implements Runnable { @Override public void run() { synchronized (obj) { System.out.println("start wait!"); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end wait!"); } } } static class NotifyThread implements Runnable { @Override public void run() { synchronized (obj) { System.out.println("start notify!"); obj.notify(); System.out.println("end notify"); } } } }
使用wait,notify來實現等待喚醒功能至少有兩個缺點:工具
而使用LockSupport的話,咱們能夠在任何場合使線程阻塞,同時也能夠指定要喚醒的線程,至關的方便。線程
public class LockSupportTest { public static void main(String[] args) { Thread parkThread = new Thread(new ParkThread()); parkThread.start(); System.out.println("開始線程喚醒"); LockSupport.unpark(parkThread); System.out.println("結束線程喚醒"); } static class ParkThread implements Runnable{ @Override public void run() { System.out.println("開始線程阻塞"); LockSupport.park(); System.out.println("結束線程阻塞"); } } }
LockSupport.park();
能夠用來阻塞當前線程,park是停車的意思,把運行的線程比做行駛的車輛,線程阻塞則至關於汽車停車,至關直觀。該方法還有個變體LockSupport.park(Object blocker)
,指定線程阻塞的對象blocker,該對象主要用來排查問題。方法LockSupport.unpark(Thread thread)
用來喚醒線程,由於須要線程做參數,因此能夠指定線程進行喚醒。code
在阻塞線程前睡眠1秒中,使喚醒動做先於阻塞發生,看看會發生什麼orm
public class LockSupportTest { public static void main(String[] args) { Thread parkThread = new Thread(new ParkThread()); parkThread.start(); System.out.println("開始線程喚醒"); LockSupport.unpark(parkThread); System.out.println("結束線程喚醒"); } static class ParkThread implements Runnable{ @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("開始線程阻塞"); LockSupport.park(); System.out.println("結束線程阻塞"); } } }
先喚醒指定線程,而後阻塞該線程,可是線程並無真正被阻塞而是正常執行完後退出了。這是怎麼回事?咱們試着在改動下代碼,先喚醒線程兩次,在阻塞線程兩次,看看會發生什麼。對象
public class LockSupportTest { public static void main(String[] args) { Thread parkThread = new Thread(new ParkThread()); parkThread.start(); for(int i=0;i<2;i++){ System.out.println("開始線程喚醒"); LockSupport.unpark(parkThread); System.out.println("結束線程喚醒"); } } static class ParkThread implements Runnable{ @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } for(int i=0;i<2;i++){ System.out.println("開始線程阻塞"); LockSupport.park(); System.out.println("結束線程阻塞"); } } } }
能夠看到線程被阻塞致使程序一直沒法結束掉。對比上面的例子,咱們能夠得出一個匪夷所思的結論,先喚醒線程,在阻塞線程,線程不會真的阻塞;可是先喚醒線程兩次再阻塞兩次時就會致使線程真的阻塞。那麼這究竟是爲何?blog
既然是淺析,那就不摳底層細節,只講關鍵,細節可能有疏漏和不到位的地方。
每一個線程都有Parker實例,以下面的代碼所示隊列
class Parker : public os::PlatformParker { private: volatile int _counter ; ... public: void park(bool isAbsolute, jlong time); void unpark(); ... } class PlatformParker : public CHeapObj<mtInternal> { protected: pthread_mutex_t _mutex [1] ; pthread_cond_t _cond [1] ; ... }
LockSupport就是經過控制變量_counter
來對線程阻塞喚醒進行控制的。原理有點相似於信號量機制。
park()
方法時,會將_counter置爲0,同時判斷前值,小於1說明前面被unpark
過,則直接退出,不然將使該線程阻塞。unpark()
方法時,會將_counter置爲1,同時判斷前值,小於1會進行線程喚醒,不然直接退出。LockSupport是JDK中用來實現線程阻塞和喚醒的工具。使用它能夠在任何場合使線程阻塞,能夠指定任何線程進行喚醒,而且不用擔憂阻塞和喚醒操做的順序,但要注意連續屢次喚醒的效果和一次喚醒是同樣的。 JDK併發包下的鎖和其餘同步工具的底層實現中大量使用了LockSupport進行線程的阻塞和喚醒,掌握它的用法和原理可讓咱們更好的理解鎖和其它同步工具的底層實現。