LockSupport理解

1、背景java

在看併發包源碼的時候看見過LockSupport,今天恰巧看到LockSupport字眼,因而看下jdk1.7中的源碼結構。想着它應該是運用多線程的鎖工具的,到底彷佛怎麼實現的呢?
多線程

2、使用併發

因而本身寫個demo對比下synchronized框架

場景:main線程中建立一個線程A,想讓threadA 循環完畢的時候先阻塞,而後再main線程中釋放。ide

1.控制多線程併發:工具

 1 public static void main(String[] args) {
 2         Object obj = new Object();
 3         generalSync(obj);
 4         obj.notifyAll();
 5         System.out.println("主線程執行完畢");
 6     }
 7 
 8     public static void generalSync(final Object obj) {
 9         Runnable runnable = new Runnable() {
10             @Override
11             public void run() {
12                 for (int i = 0; i < 10000; i++) {
13              System.out.println(i);
14                 }
15                 System.out.println("循環完畢");
16                 try {
17                     obj.wait();
18                 } catch (InterruptedException e) {
19                     e.printStackTrace();
20                 }
21                 System.out.println("線程A執行完畢");
22             }
23         };
24         Thread threadA = new Thread(runnable);
25         threadA.start();
26     }

上面這個最終結果是什麼呢情?會以異常結束:java.lang.IllegalMonitorStateException, 產生的緣由是Object的wait,notify,notifyAll方法必須在同步塊中執行spa

2.使用synchronized但主線程接觸阻塞在threadA阻塞執行以前線程

 1 public static void main(String[] args) {
 2         Object obj = new Object();
 3         generalSync(obj);
       // Thread.sleep(1000);
4 synchronized (obj) { 5 obj.notifyAll(); 6 } 7 System.out.println("主線程執行完畢"); 8 } 9 10 public static void generalSync(final Object obj) { 11 Runnable runnable = new Runnable() { 12 @Override 13 public void run() { 14 for (int i = 0; i < 10000; i++) { 15 System.out.println(i); 16 } 17 System.out.println("循環完畢"); 18 try { 19 synchronized (obj) { 20 obj.wait(); 21 } 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 System.out.println("線程A執行完畢"); 26 } 27 }; 28 Thread threadA = new Thread(runnable); 29 threadA.start(); 30 } 31

上面會致使一直阻塞,由於主線程在第3行開啓一個threadA後,就往下執行4~7代碼,而threadA須要循環所用的時間更久。所以會先調用obj.notifyAll(),而後再obj.wait()致使調用順序錯誤一直阻塞。想要達到咱們預期的目的,根據threadA時間,能夠在第4行添加Thread.sleep(1000)使主線程中的obj.notifyAll()晚於obj.wait()執行。code

3.使用LockSupport來實現同步對象

 1     public static void main(String[] args) {
 2         Thread threadA = generalSync();
 3         LockSupport.unpark(threadA);
 4         System.out.println("主線程執行完畢");
 5     }
 6 
 7     public static Thread generalSync() {
 8         Runnable runnable = new Runnable() {
 9             @Override
10             public void run() {
11                 for (int i = 0; i < 10000; i++) {
12                     System.out.println(i);
13                 }
14                 System.out.println("循環完畢");
15                 LockSupport.park();
16                 System.out.println("線程A執行完畢");
17             }
18         };
19         Thread threadA = new Thread(runnable);
20         threadA.start();
21         return threadA;
22     }

經過LockSupport無需關於阻塞和釋放的調用前後問題,僅僅經過park/unpark便可阻塞或釋放。park/unpark模型真正解耦了線程之間的同步,線程之間再也不須要一個Object或者其它變量來存儲狀態,再也不須要關心對方的狀態

3、閱讀源碼

經過類註釋介紹,LockSupport是JDK中比較底層的類,用來建立鎖和其餘同步工具類的基本線程阻塞原語。java鎖和同步器框架的核心 AQS: AbstractQueuedSynchronizer,就是經過調用 LockSupport .park()和 LockSupport .unpark()實現線程的阻塞和喚醒 的,下面重點關注着2個方法。

1 public class LockSupport {
2     private LockSupport() {} // Cannot be instantiated.
3 
4     // Hotspot implementation via intrinsics API
5     private static final Unsafe unsafe = Unsafe.getUnsafe();
6     private static final long parkBlockerOffset;

1.根據上面的類定義,內部構造方法私有化,外部類沒法實例,做爲工具類使用的意圖。類中實例化了一個Unsafe這個可直接操做內存的本地API

1    public static void park(Object blocker) {
2         Thread t = Thread.currentThread();
3         setBlocker(t, blocker);
4         unsafe.park(false, 0L);
5         setBlocker(t, null);
6     }

2.阻塞是經過park方法,第2行獲取當前線程;第3行執行setBlocker(Thread t, Object arg),setBlocker從名字感受設置阻塞的地方;第4行調用Unsafe中public native void park(boolean paramBoolean, long paramLong)產生阻塞;第5行調用setBlocker(Thread t, Object arg),可是Object參數是null,此處應該是去除阻塞點。

 1 public class LockSupport {
 2     private LockSupport() {} // Cannot be instantiated.
 3 
 4     // Hotspot implementation via intrinsics API
 5     private static final Unsafe unsafe = Unsafe.getUnsafe();
 6     private static final long parkBlockerOffset;
 7 
 8     static {
 9         try {
10             parkBlockerOffset = unsafe.objectFieldOffset
11                 (java.lang.Thread.class.getDeclaredField("parkBlocker"));
12         } catch (Exception ex) { throw new Error(ex); }
13     }
14 
15     private static void setBlocker(Thread t, Object arg) {
16         // Even though volatile, hotspot doesn't need a write barrier here.
17         unsafe.putObject(t, parkBlockerOffset, arg);
18     }

3.setBlocker是調用Unsafe中public native void putObject(Object paramObject1, long paramLong, Object paramObject2)方法,這個long類型paramLong傳的是Thread在內存中的偏移量。經過8~13能夠看出在靜態代碼塊中經過Unsafe中public native long objectFieldOffset(Field paramField)來獲得內存中位置(以前原子操做類也是經過此方法來獲取偏移量)這個偏移量屬性名稱是"parkBlocker",類型從第11行能夠知道parkBlocker是java.lang.Thread類中的一個屬性,

public
class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    ... ...
 /**
     * The argument supplied to the current call to
     * java.util.concurrent.locks.LockSupport.park.
     * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
     * Accessed using java.util.concurrent.locks.LockSupport.getBlocker
     */
    volatile Object parkBlocker;

這個parkBlocker應該是Thread類變量。首先LockSupport 是構造方法私有化,相似一個工具類,並且parkBlockerOffset是static類型的,因此即便在多個場景下的多線程環境,不一樣的多個Thread調用setBlocker方法都只是針對Thread的類變量進行賦值(類變量只有一個)因此是多對一的關係。而且經過getBlocker源碼註釋能夠看出

 1     /**
 2      * Returns the blocker object supplied to the most recent
 3      * invocation of a park method that has not yet unblocked, or null
 4      * if not blocked.  The value returned is just a momentary
 5      * snapshot -- the thread may have since unblocked or blocked on a
 6      * different blocker object.
 7      *
 8      * @param t the thread
 9      * @return the blocker
10      * @throws NullPointerException if argument is null
11      * @since 1.6
12      */
13     public static Object getBlocker(Thread t) {
14         if (t == null)
15             throw new NullPointerException();
16         return unsafe.getObjectVolatile(t, parkBlockerOffset);
17     }

從註釋看出「返回的是最近被阻塞的對象,相似一個瞬間的快照」,那麼我理解Thread中Object parkBlocker屬性可能會被多個線程賦值。這個屬性跟併發阻塞並沒有關係,只是起到記錄阻塞對象的做用。

 至於Unsafe類中native方法,就沒有去追究。看其餘博客理解到的是:在hotspot裏每一個java線程都有一個Parker實例,Parker裏使用了一個無鎖的隊列在分配釋放Parker實例。Parker裏面有個變量,volatile int _counter 至關於許可,二元信號量(相似0和1)

當調用park時,先嚐試直接可否直接拿到「許可」(即_counter>0時)若是成功,則把_counter設置爲0,並返回;若是不成功,則構造一個ThreadBlockInVM,而後檢查_counter是否是>0,若是是,則把_counter設置爲0

當unpark時,則簡單多了,直接設置_counter爲1,若是_counter以前的值是0,則還要調用喚醒在park中等待的線程:

相關文章
相關標籤/搜索