Java中的隊列同步器AQS

1、AQS概念

  一、隊列同步器是用來構建鎖或者其餘同步組件的基礎框架,使用一個int型變量表明同步狀態,經過內置的隊列來完成線程的排隊工做。java

  二、下面是JDK8文檔中對於AQS的部分介紹node

  public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements Serializable
  提供一個框架,用於實現依賴先進先出(FIFO)等待隊列的阻塞鎖和相關同步器(信號量,事件等)。 該類被設計爲大多數類型的同步器的有用依據,這些同步器依賴於單個原子int值來表示狀
態。子類必須定義改變此狀態的protected方法,以及根據該對象被獲取或釋放來定義該狀態的含義。給定這些,這個類中的其餘方法執行全部排隊和阻塞機制。 子類能夠保持其餘狀態字段,但只以
原子方式更新int使用方法操縱值getState() , setState(int)和compareAndSetState(int, int)被跟蹤相對於同步。   
此類支持默認獨佔模式和共享模式。 當以獨佔模式獲取時,嘗試經過其餘線程獲取不能成功。 多線程獲取的共享模式可能(但不須要)成功。 除了在機械意義上,這個類不理解這些差別,當共享
模式獲取成功時,下一個等待線程(若是存在)也必須肯定它是否也能夠獲取。 在不一樣模式下等待的線程共享相同的FIFO隊列。 一般,實現子類只支持這些模式之一,可是二者均可以在
ReadWriteLock中發揮做用。僅支持獨佔或僅共享模式的子類不須要定義支持未使用模式的方法。

  總結來講就是:安全

  ①子類經過繼承AQS並實現其抽象方法來管理同步狀態,對於同步狀態的更改經過提供的getState()、setState(int state)、compareAndSetState(int expect, int update)來進行操做,由於使用CAS操做保證同步狀態的改變是原子的多線程

  ②子類被推薦定義爲自定義同步組件的靜態內部類,同步器自己並無實現任何的同步接口,僅僅是定義了若干狀態獲取和釋放的方法來提供自定義同步組件的使用。併發

  ③同步器既能夠支持獨佔式的獲取同步狀態,也能夠支持共享式的獲取同步狀態(ReentrantLock、ReentrantReadWriteLock、CountDownLatch等不一樣類型的同步組件)框架

  三、同步器是實現鎖的關鍵,在鎖的實現中聚合同步器,利用同步器實現鎖的語義;ide

2、AQS的接口和實例

一、同步器的設計實現原理

  繼承同步器而且重寫指定的方法,而後將同步器組合在自定義同步組件的實現中,而且調用同步器提供的模板方法(這些模板方法會調用重寫的方法);而重寫指定的方法的時候,須要使用getState()、setState(int state)、compareAndSetState(int expect, int update)來訪問或者更新同步狀態。下面是源碼中state變量和三個方法的定義聲明實現測試

 1   /**
 2      * .(同步狀態)
 3      */
 4     private volatile int state;
 5 
 6     /**
 7      * (返回當前的同步狀態)
 8      * 此操做的內存語義爲@code volatile read
 9      */
10     protected final int getState() {
11         return state;
12     }
13 
14     /**
15      * (設置新的同步狀態)
16      * 此操做的內存語義爲@code volatile read
17      */
18     protected final void setState(int newState) {
19         state = newState;
20     }
21 
22     /**
23      * (若是要更新的狀態和指望的狀態相同,那就經過原子的方式更新狀態)
24      * ( 此操做的內存語義爲@code volatile read 和 write)
25      * (若是更新的狀態和指望的狀態不一樣就返回false)
26      */
27     protected final boolean compareAndSetState(int expect, int update) {
28         return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
29     }

 

 

二、下面介紹AQS提供可被重寫的方法

 1 /**
 2  * 獨佔式的獲取同步狀態,實現該方法須要查詢當前狀態並判斷同步狀態是否符合預期,而後再進行CAS設置同步狀態
 3  *
 4  */
 5 protected boolean tryAcquire(int arg) {
 6     throw new UnsupportedOperationException();
 7 }
 8 
 9 /**
10  * 獨佔式的釋放同步狀態,等待獲取同步狀態的線程能夠有機會獲取同步狀態
11  *
12  */
13 protected boolean tryRelease(int arg) {
14     throw new UnsupportedOperationException();
15 }
16 
17 /**
18  * 嘗試以共享模式獲取。 該方法應該查詢對象的狀態是否容許在共享模式下獲取該對象,若是是這樣,就能夠獲取它。 該方法老是由執行獲取的線程調用。
19  * 若是此方法報告失敗,則獲取方法可能將線程排隊(若是還沒有排隊),直到被其餘線程釋放爲止。 獲取失敗時返回負值,若是在獲取成共享模式下功但沒
20  * 有後續共享模式獲取能夠成功,則爲零; 而且若是以共享模式獲取成功而且隨後的共享模式獲取可能成功,則爲正值,在這種狀況下,後續等待線程必須檢查可用性。
21  */
22 protected int tryAcquireShared(int arg) {
23     throw new UnsupportedOperationException(); //若是不支持共享模式 ,會拋出該異常
24 }
25 
26 /**
27  * 嘗試將狀態設置爲以共享模式釋放同步狀態。 該方法老是由執行釋放的線程調用。 
28  */
29 protected int tryReleaseShared(int arg) {
30     throw new UnsupportedOperationException(); //若是不支持共享模式 ,會拋出該異常
31 }
32 
33 /**
34  * 當前同步器是否在獨佔模式下被線程佔用,通常該方法表示是否被當前線程所獨佔
35  */
36 protected int isHeldExclusively(int arg) {
37     throw new UnsupportedOperationException(); //若是不支持共享模式 ,會拋出該異常
38 }

三、同步器提供的模板方法

  在實現自定義同步組件的時候,須要重寫上面的方法,而下面的模板方法會調用上面重寫的方法。下面介紹同步器提供的模板方法ui

 1 /**
 2  * 以獨佔模式獲取,忽略中斷。 經過調用至少一次tryAcquire(int)實現,成功返回。 不然線 
 3  * 程排隊,可能會重複阻塞和解除阻塞,直到成功才調用tryAcquire(int) 
 4  */
 5 public final void acquire(int arg) {...}
 6 
 7 /**
 8  * 以獨佔方式得到,若是中斷,停止。 經過首先檢查中斷狀態,而後調用至少一次
 9  * tryAcquire(int) ,成功返回。 不然線程排隊,可能會重複阻塞和解除阻塞,調用
10  * tryAcquire(int)直到成功或線程中斷。
11  */
12 public final void acquireInterruptibly(int arg) throws InterruptedException {...}
13 
14 /**
15  * 嘗試以獨佔模式獲取,若是中斷則停止,若是給定的超時時間失敗。 首先檢查中斷狀態,然
16  * 後調用至少一次tryAcquire(int) ,成功返回。 不然,線程排隊,可能會重複阻塞和解除阻
17  * 塞,調用tryAcquire(int)直到成功或線程中斷或超時
18  */
19 public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {...}
20 
21 /**
22  * 以共享模式獲取,忽略中斷。 經過首次調用至少一次執行 tryAcquireShared(int),成功返
23  * 回。 不然線程排隊,可能會重複阻塞和解除阻塞,直到成功調用tryAcquireShared(int) 。
24  */
25 public final void acquireShared(int arg){...}
26 
27 /**
28  * 以共享方式獲取,若是中斷,停止。 首先檢查中斷狀態,而後調用至少一次
29  * tryAcquireShared(int) ,成功返回。 不然線程排隊,可能會重複阻塞和解除阻塞,調用
30  * tryAcquireShared(int)直到成功或線程中斷。
31  */
32 public final void acquireSharedInterruptibly(int arg) throws InterruptedException{...}
33 
34 /**
35  * 嘗試以共享模式獲取,若是中斷則停止,若是給定的時間超過,則失敗。 經過首先檢查中斷 
36  * 狀態,而後調用至少一次tryAcquireShared(int) ,成功返回。 不然,線程排隊,可能會重 
37  * 復阻塞和解除阻塞,調用tryAcquireShared(int)直到成功或線程中斷或超時。 
38  */
39 public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException{...}
40 
41 /**
42  * 獨佔式的釋放同步狀態,該方法會在釋放同步狀態以後,將同步隊列中的第一個節點包含的線程喚醒
43  */
44 public final boolean release(int arg){...}
45 
46 /**
47  * 共享式的釋放同步狀態
48  */
49 public final boolean releaseShared(int arg){...}
50 
51 /**
52  * 獲取在等待隊列上的線程集合
53  */
54 public final Collection<Thread> getQueuedThreads(){...}

3、隊列同步器的實現分析

 一、同步隊列

a)t同步隊列的實現原理this

  AQS內部維護一個同步隊列來完成同步狀態的管理,當前線程獲取同步狀態失敗的時候,AQS會將當前線程以及等待狀態信息構形成一個結點Node並將其加入同步隊列中,同時阻塞當前線程,當同步狀態由持有線程釋放的時候,會將同步隊列中的首節點喚醒使其再次嘗試獲取同步狀態。同步隊列中的結點用來保存獲取同步狀態失敗的線程的線程引用、等待狀態以及前驅結點和後繼結點。下面是Node的屬性分析

 1     static final class Node {
 2         /** 共享模式下構造結點 */
 3         static final Node SHARED = new Node();
 4         /** 獨佔模式下構造結點 */
 5         static final Node EXCLUSIVE = null;
 6 
 7         /** 用於指示線程已經取消的waitStatus值(因爲在同步隊列中等待的線程等待超時或者發生中斷,須要從同步隊列中取消等待,結點進入該狀態將不會發生變化)*/
 8         static final int CANCELLED =  1;
 9         /** waitstatus值指示後續線程須要取消等待(後繼結點的線程處於等待狀態,而當前結點的線程若是釋放了同步狀態或者CANCELL,將會通知後繼結點的線程以運行) */
10         static final int SIGNAL    = -1;
11         /**waitStatus值表示線程正在等待條件(本來結點在等待隊列中,結點線程等待在Condition上,當其餘線程對Condition調用了signal()方法以後)該結點會從
         等待隊列中轉移到同步隊列中,進行同步狀態的獲取
*/ 12 static final int CONDITION = -2; 13 /** 14 * waitStatus值表示下一個共享式同步狀態的獲取應該無條件傳播下去 15 */ 16 static final int PROPAGATE = -3; 17 18 /** 19 * 不一樣的等到狀態的int值 20 */ 21 volatile int waitStatus; 22 23 /** 24 * 前驅結點,當結點加入同步隊列將會被設置前驅結點信息 25 */ 26 volatile Node prev; 27 28 /** 29 * 後繼結點 30 */ 31 volatile Node next; 32 33 /** 34 * 當前獲取到同步狀態的線程 35 */ 36 volatile Thread thread; 37 38 /** 39 * 等待隊列中的後繼結點,若是當前結點是共享的,那麼這個字段是一個SHARED常量;也就是說結點類型(獨佔和共享)和等待隊列中的後繼結點公用一個字段 40 */ 41 Node nextWaiter; 42 43 /** 44 * 若是是共享模式下等待,那麼返回true(由於上面的Node nextWaiter字段在共享模式下是一個SHARED常量) 45 */ 46 final boolean isShared() { 47 return nextWaiter == SHARED; 48 } 49 50 final Node predecessor() throws NullPointerException { 51 Node p = prev; 52 if (p == null) 53 throw new NullPointerException(); 54 else 55 return p; 56 } 57 58 Node() { // 用於創建初始頭結點或SHARED標記 59 } 60 61 Node(Thread thread, Node mode) { // 用於添加到等待隊列 62 this.nextWaiter = mode; 63 this.thread = thread; 64 } 65 66 Node(Thread thread, int waitStatus) { // Used by Condition 67 this.waitStatus = waitStatus; 68 this.thread = thread; 69 } 70 }

b)同步隊列示意圖和簡單分析

  ①同步隊列示意圖:當一個線程獲取了同步狀態後,其餘線程不能獲取到該同步狀態,就會被構造稱爲Node而後添加到同步隊列之中,這個添加的過程基於CAS保證線程安全性。

  ②同步隊列遵循先進先出(FIFO),首節點是獲取到同步狀態的結點,首節點的線程在釋放同步狀態的時候將會喚醒後繼結點(而後後繼結點就會變成新的首節點等待獲取同步狀態)

二、獨佔式同步狀態的獲取和釋放

  ①前面說過,同步器的acquire()方法會獲取同步狀態,這個方法對不會響應中斷,也就是說當線程獲取通同步狀態失敗後會被構形成結點加入到同步隊列中,當線程被中斷時不會從同步隊列中移除。

 1 /**
 2  * ①首先調用tryAcquire方法嘗試獲取同步狀態,若是獲取同步狀態失敗,就進行下面的操做
 3  * ②獲取失敗:按照獨佔式的模式構造同步結點並經過addWaiter方法將結點添加到同步隊列的尾部
 4  * ③經過acquireQueue方法自旋獲取同步狀態。
 5  * ④若是獲取不到同步狀態,就阻塞結點中的線程,而結點中的線程喚醒主要是經過前驅結點的出隊或者被中斷來實現
 6  */
 7 public final void acquire(int arg) {
 8     if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
 9         selfInterrupt();
10 }

   ②下面是addWaiter、enq和自旋獲取同步狀態acquireQueue方法的實現(該方法的主要做用就是將獲取同步狀態失敗的線程構形成結點而後添加到同步隊列的隊尾)

 1 private Node addWaiter(Node mode) {
 2     Node node = new Node(Thread.currentThread(), mode);
 3     //嘗試直接放在隊尾
 4     Node pred = tail; //直接獲取同步器的tail結點
 5     if (pred != null) { 
 6         node.prev = pred; 
 7         if (compareAndSetTail(pred, node)) {
 8             //隊尾結點不爲空經過原子操做將構造的結點置爲隊尾結點
 9             pred.next = node;
10             return node;
11         }
12     }
13     //採用自旋方式保證構造的結點添加到同步隊列中
14     enq(node);
15     return node;
16 }
17 private Node enq(final Node node) {
18     for (;;) { //死循環知道添加成功
19         Node t = tail;
20         if (t == null) { // Must initialize
21             if (compareAndSetHead(new Node()))
22                 tail = head;
23         } else {
24             node.prev = t;
25             //經過CAS方式將結點添加到同步隊列以後纔會返回,不然就會不斷嘗試添加(這樣實際上就是在併發狀況下,把向同步隊列添加Node變得串行化了)
26             if (compareAndSetTail(t, node)) {
27                 t.next = node;
28                 return t;
29             }
30         }
31     }
32 }    
33 /**
34  *     經過tryAcquire()和addWaiter(),表示該線程獲取同步狀態已經失敗,被放入同步        
35  * 隊列尾部了。線程阻塞等待直到其餘線程(前驅結點得到同步裝填或者被中斷)釋放同步狀
36  * 態後喚醒本身,本身才能得到。
37  */
38 final boolean acquireQueued(final Node node, int arg) {
39     boolean failed = true;
40     try {
41         boolean interrupted = false;
42         //線程在死循環的方式中嘗試獲取同步狀態
43         for (;;) {
44             final Node p = node.predecessor(); //獲取前驅結點
45             //只有前驅接待是頭結點的時候才能嘗試獲取同步狀態
46             if (p == head && tryAcquire(arg)) {
47                 setHead(node); //獲取到同步狀態以後,就將本身設置爲頭結點
48                 p.next = null; //前驅結點已經得到同步狀態去執行本身的程序了,因此須要釋放掉佔用的同步隊列的資源,由JVM回收
49                 failed = false;
50                 return interrupted;
51             }
52             //若是獲取同步狀態失敗,應該自旋等待繼續獲取而且校驗本身的中斷標誌位信息
53             if (shouldParkAfterFailedAcquire(p, node) &&
54                 parkAndCheckInterrupt())
55                 interrupted = true; //若是被中斷,就改變本身的中斷標誌位狀態信息
56         }
57     } finally {
58         if (failed)
59             cancelAcquire(node);
60     }
61 }

  ③獨佔式獲取同步狀態的整個流程

  ④獨佔式同步器的釋放:release方法執行時,會喚醒頭結點的後繼結點線程

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;//頭結點
        //喚醒頭結點的後繼結點線程
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

三、共享式同步狀態的獲取和釋放 

  ①共享式獲取和獨佔式獲取最主要的區別是可否有多個線程同時獲取到同步狀態。如圖所示簡易描述兩者的區別(共享式訪問的時候,能夠容許多個線程訪問資源,可是存在獨佔式訪問的時候,同一時刻其餘的不論是共享仍是獨佔都會被阻塞)

  ②關於共享式獲取同步狀態的方法

 1 /**
 2  * 此方法是共享模式下線程獲取共享同步狀態的頂層入口。它會嘗試去獲取同步狀態,獲取成功則直接返回,
 3  * 獲取失敗則進入等待隊列一直嘗試獲取(執行doAcquireShared方法體中的內容),直到獲取到資源爲止(條件就是tryAcquireShared方法返回值大於等於0),整個過程忽略中斷
 4  */
 5 public final void acquireShared(int arg) {
 6     if (tryAcquireShared(arg) < 0)
 7         doAcquireShared(arg);
 8 } 
 9 /**
10  * "自旋"嘗試獲取同步狀態
11  */
12 private void doAcquireShared(int arg) {
13     //首先將該線程包括線程引用、等待狀態、前驅結點和後繼結點的信息封裝臺Node中,而後添加到等待隊列裏面(一共享模式添加)
14     final Node node = addWaiter(Node.SHARED);
15     boolean failed = true;
16     try {
17         boolean interrupted = false; //當前線程的中斷標誌
18         for (;;) {
19             final Node p = node.predecessor(); //獲取前驅結點
20             if (p == head) {
21                 //當前驅結點是頭結點的時候就會以共享的方式去嘗試獲取同步狀態
22                 int r = tryAcquireShared(arg); 
23                 //判斷tryAcquireShared的返回值
24                 if (r >= 0) {
25                     //若是返回值大於等於0,表示獲取同步狀態成功,就修改當前的頭結點並將信息傳播都後續的結點隊列中
26                     setHeadAndPropagate(node, r);
27                     p.next = null; // 釋放掉已經獲取到同步狀態的前驅結點的資源
28                     if (interrupted)
29                         selfInterrupt(); //檢查中斷標誌
30                     failed = false;
31                     return;
32                 }
33             }
34             if (shouldParkAfterFailedAcquire(p, node) &&
35                 parkAndCheckInterrupt())
36                 interrupted = true;
37         }
38     } finally {
39         if (failed)
40             cancelAcquire(node);
41     }
42 }

  根據源代碼咱們能夠了解共享式獲取同步狀態的整個過程

  首先同步器會調用tryAcquireShared方法來嘗試獲取同步狀態,而後根據這個返回值來判斷是否獲取到同步狀態(當返回值大於等於0可視爲獲取到同步狀態);若是第一次獲取失敗的話,就進入'自旋'狀態(執行doAcquireShared方法)一直嘗試去獲取同步狀態;在自旋獲取中,若是檢查到當前前驅結點是頭結點的話,就會嘗試獲取同步狀態,而一旦獲取成功(tryAcquireShared方法返回值大於等於0)就能夠從自旋狀態退出。

  另外,還有一點就是上面說到的一個處於等待隊列的線程要想開始嘗試去獲取同步狀態,須要知足的條件就是前驅結點是頭結點,那麼它自己就是整個隊列中的第二個結點。當頭結點釋放掉全部的臨界資源以後,咱們考慮每一個線程運行所需資源的不一樣數量問題,以下圖所示

  ③共享式同步狀態的釋放

  對於支持共享式的同步組件(即多個線程同同時訪問),它們和獨佔式的主要區別就是tryReleaseShared方法必須確保同步狀態的釋放是線程安全的(CAS的模式來釋放同步狀態,由於既然是多個線程可以訪問,那麼釋放的時候也會是多個線程的,就須要保證釋放時候的線程安全)

 1 /**
 2  * 該方法是共享模式下線程釋放共享資源的頂層入口。它會釋放指定量的資源,若是成功釋放且容許喚醒等待線程,它會喚醒等待隊列裏的其餘線程來獲取資源。
 3  */
 4 public final boolean releaseShared(int arg) {
 5     if (tryReleaseShared(arg)) {
 6         doReleaseShared(); //
 7         return true;
 8     }
 9     return false;
10 }

4、自定義同步組件的實現

  一、共享式鎖的實現

  ①、自定義一個同步組件,能夠容許兩個線程訪問(共享式同步組件),超過兩個線程就會被阻塞。

  ②、既然是共享式同步組件,按照前面所說的,組件自己須要使用AQS提供的共享式模板方法acquireShared等;組件的內部類須要實現AQS,而且重寫關於共享式獲取同步狀態的方法(tryAcquireShared()、tryReleaseShared()等共享模式下的方法)。

  ③、既然是兩個線程可以同時訪問的話,那麼狀態數的取值範圍就是0、一、2了,每當一個線程獲取到同步狀態的時候state值減1,反之就會增長1;當state值爲0的時候就會阻塞其餘想要獲取同步狀態的線程。對於同步狀態的更改須要使用CAS來進行保證原子性。

 1 package cn.source.concurrent;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.locks.AbstractQueuedSynchronizer;
 5 import java.util.concurrent.locks.Condition;
 6 import java.util.concurrent.locks.Lock;
 7 
 8 public class TestAQS implements Lock{
 9 
10     private Sync sync = new Sync(2);
11     
12     private static class Sync extends AbstractQueuedSynchronizer {
13         
14         Sync(int num) {
15             if(num <= 0) {
16                 throw new RuntimeException("num須要大於0");
17             }
18             setState(num);
19         }
20 
21         @Override
22         protected int tryAcquireShared(int arg) {
23             for(; ;) {
24                 int currentState = getState();
25                 int newState = currentState - arg;
26                 if(newState < 0 || compareAndSetState(currentState, newState)) {
27                     return newState;
28                 }
29             }
30         }
31 
32         @Override
33         protected boolean tryReleaseShared(int arg) {
34             for(; ;) {
35                 int currentState = getState();
36                 int newState = currentState + arg;
37                 if(compareAndSetState(currentState, newState)) {
38                     return true;
39                 }
40             }
41         }
42         
43         
44     }
45     @Override
46     public void lock() {
47         sync.acquireShared(1);
48     }
49 
50     @Override
51     public void unlock() {
52         sync.releaseShared(1);
53     }
54 
55     //......
56 }
共享式鎖
 1 /**
 2  * 測試結果:輸出的線程名稱是成對的,保證同一時刻只有兩個線程可以獲取到鎖
 3  *
 4  */         
 5 public class TestLockShare {
 6     @Test
 7     public void test() {
 8         Lock lock = new TestAQS();
 9         class Worker extends Thread {
10 
11             @Override
12             public void run() {
13                 while(true) {
14                     lock.lock();
15                     try {
16                         Thread.sleep(1000);
17                         System.out.println(Thread.currentThread().getName());
18                         Thread.sleep(1000);
19                     } catch (Exception e) {
20                         e.printStackTrace();
21                     } finally {
22                         lock.unlock();
23                     }
24                 }
25             }
26             
27         }
28         
29         for (int i = 0; i < 8; i++) {
30             Worker worker = new Worker();
31             worker.setDaemon(true);
32             worker.start();
33             
34         }
35         for (int i = 0; i < 8; i++) {
36             try {
37                 Thread.sleep(1000);
38             } catch (InterruptedException e) {
39                 // TODO Auto-generated catch block
40                 e.printStackTrace();
41             }
42             System.out.println();
43         }
44     }
45 }
共享式鎖測試

  二、獨佔式鎖的實現

 1 package cn.source.concurrent;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.locks.AbstractQueuedSynchronizer;
 5 import java.util.concurrent.locks.Condition;
 6 import java.util.concurrent.locks.Lock;
 7 
 8 public class Mutex implements Lock{
 9 
10     private Sync sync = new Sync();
11     
12     private static class Sync extends AbstractQueuedSynchronizer {
13 
14         /**
15          * 嘗試獲取資源,當即返回。成功則返回true,不然false。
16          */
17         @Override
18         protected boolean tryAcquire(int arg) {
19             if(compareAndSetState(0, 1)) {//state爲0才設置爲1,不可重入!
20                 setExclusiveOwnerThread(Thread.currentThread());//設置爲當前線程獨佔資源
21                 return true;
22             }
23             return false;
24         }
25 
26         /**
27          * 嘗試釋放資源,當即返回。成功則爲true,不然false。
28          */
29         @Override
30         protected boolean tryRelease(int arg) {
31             if(getState() == 0) { //既然來釋放,那確定就是已佔有狀態了。只是爲了保險,多層判斷!
32                 throw new IllegalMonitorStateException();
33             }
34             setExclusiveOwnerThread(null);
35             setState(0);
36             return true;
37         }
38 
39         @Override
40         protected boolean isHeldExclusively() {
41             // 判斷是否鎖定狀態
42             return getState() == 1;
43         }
44         
45     }
46     
47     @Override
48     public void lock() {
49         sync.acquire(1);
50     }
51 
52     @Override
53     public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
54         return sync.tryAcquire(1);
55     }
56 
57     @Override
58     public void unlock() {
59         sync.release(1);
60     }
61 
62 }
獨佔式鎖
 1 public class TestMutex {
 2     @Test
 3     public void test() {
 4         Lock lock = new Mutex();
 5         class Worker extends Thread {
 6 
 7             @Override
 8             public void run() {
 9                 while(true) {
10                     lock.lock();
11                     try {
12                         Thread.sleep(1000);
13                         System.out.println(Thread.currentThread().getName());
14                         Thread.sleep(1000);
15                     } catch (Exception e) {
16                         e.printStackTrace();
17                     } finally {
18                         lock.unlock();
19                     }
20                 }
21             }
22             
23         }
24         
25         for (int i = 0; i < 8; i++) {
26             Worker worker = new Worker();
27             worker.setDaemon(true);
28             worker.start();
29             
30         }
31         for (int i = 0; i < 8; i++) {
32             try {
33                 Thread.sleep(1000);
34             } catch (InterruptedException e) {
35                 e.printStackTrace();
36             }
37             System.out.println();
38         }
39     }
40 }
獨佔式鎖測試
相關文章
相關標籤/搜索