<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    xylz,imxylz

    關注后端架構、中間件、分布式和并發編程

       :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      111 隨筆 :: 10 文章 :: 2680 評論 :: 0 Trackbacks

    在理解J.U.C原理以及鎖機制之前,我們來介紹J.U.C框架最核心也是最復雜的一個基礎類:java.util.concurrent.locks.AbstractQueuedSynchronizer


    AQS

    AbstractQueuedSynchronizer,簡稱AQS,是J.U.C最復雜的一個類,導致絕大多數講解并發原理或者實戰的時候都不會提到此類。但是虛心的作者愿意借助自己有限的能力和精力來探討一二(參考資源中也有一些作者做了部分的分析。)。

    首先從理論知識開始,在了解了相關原理后會針對源碼進行一些分析,最后加上一些實戰來描述。

    image

    上面的繼承體系中,AbstractQueuedSynchronizer是CountDownLatch/FutureTask/ReentrantLock/RenntrantReadWriteLock/Semaphore的基礎,因此AbstractQueuedSynchronizer是Lock/Executor實現的前提。公平鎖、不公平鎖、Condition、CountDownLatch、Semaphore等放到后面的篇幅中說明。

    完整的設計原理可以參考Doug Lea的論文 The java.util.concurrent Synchronizer Framework ,這里做一些簡要的分析。

    基本的思想是表現為一個同步器,支持下面兩個操作:

    獲取鎖:首先判斷當前狀態是否允許獲取鎖,如果是就獲取鎖,否則就阻塞操作或者獲取失敗,也就是說如果是獨占鎖就可能阻塞,如果是共享鎖就可能失敗。另外如果是阻塞線程,那么線程就需要進入阻塞隊列。當狀態位允許獲取鎖時就修改狀態,并且如果進了隊列就從隊列中移除。

    while(synchronization state does not allow acquire){

        enqueue current thread if not already queued;

        possibly block current thread;

    }

    dequeue current thread if it was queued;

    釋放鎖:這個過程就是修改狀態位,如果有線程因為狀態位阻塞的話就喚醒隊列中的一個或者更多線程。

    update synchronization state;

    if(state may permit a blocked thread to acquire)

        unlock one or more queued threads;

    要支持上面兩個操作就必須有下面的條件:

    • 原子性操作同步器的狀態位
    • 阻塞和喚醒線程
    • 一個有序的隊列

    目標明確,要解決的問題也清晰了,那么剩下的就是解決上面三個問題。

    狀態位的原子操作

    這里使用一個32位的整數來描述狀態位,前面章節的原子操作的理論知識整好派上用場,在這里依然使用CAS操作來解決這個問題。事實上這里還有一個64位版本的同步器(AbstractQueuedLongSynchronizer),這里暫且不談。

    阻塞和喚醒線程

    標準的JAVA API里面是無法掛起(阻塞)一個線程,然后在將來某個時刻再喚醒它的。JDK 1.0的API里面有Thread.suspend和Thread.resume,并且一直延續了下來。但是這些都是過時的API,而且也是不推薦的做法。

    在JDK 5.0以后利用JNI在LockSupport類中實現了此特性。

    LockSupport.park()
    LockSupport.park(Object)
    LockSupport.parkNanos(Object, long)
    LockSupport.parkNanos(long)
    LockSupport.parkUntil(Object, long)
    LockSupport.parkUntil(long)
    LockSupport.unpark(Thread)

    上面的API中park()是在當前線程中調用,導致線程阻塞,帶參數的Object是掛起的對象,這樣監視的時候就能夠知道此線程是因為什么資源而阻塞的。由于park()立即返回,所以通常情況下需要在循環中去檢測競爭資源來決定是否進行下一次阻塞。park()返回的原因有三:

    • 其他某個線程調用將當前線程作為目標調用 unpark;
    • 其他某個線程中斷當前線程;
    • 該調用不合邏輯地(即毫無理由地)返回。

    其實第三條就決定了需要循環檢測了,類似于通常寫的while(checkCondition()){Thread.sleep(time);}類似的功能。

    有序隊列

    在AQS中采用CHL列表來解決有序的隊列的問題。

    image AQS采用的CHL模型采用下面的算法完成FIFO的入隊列和出隊列過程。

    對于入隊列(enqueue):采用CAS操作,每次比較尾結點是否一致,然后插入的到尾結點中。

    do {

            pred = tail;

    }while ( !compareAndSet(pred,tail,node) );

    對于出隊列(dequeue):由于每一個節點也緩存了一個狀態,決定是否出隊列,因此當不滿足條件時就需要自旋等待,一旦滿足條件就將頭結點設置為下一個節點。

    while (pred.status != RELEASED) ;

    head  = node;

    實際上這里自旋等待也是使用LockSupport.park()來實現的。

    AQS里面有三個核心字段:

    private volatile int state;

    private transient volatile Node head;

    private transient volatile Node tail;

    其中state描述的有多少個線程取得了鎖,對于互斥鎖來說state<=1。head/tail加上CAS操作就構成了一個CHL的FIFO隊列。下面是Node節點的屬性。

    volatile int waitStatus; 節點的等待狀態,一個節點可能位于以下幾種狀態:

    • CANCELLED = 1: 節點操作因為超時或者對應的線程被interrupt。節點不應該留在此狀態,一旦達到此狀態將從CHL隊列中踢出。
    • SIGNAL = -1: 節點的繼任節點是(或者將要成為)BLOCKED狀態(例如通過LockSupport.park()操作),因此一個節點一旦被釋放(解鎖)或者取消就需要喚醒(LockSupport.unpack())它的繼任節點。
    • CONDITION = -2:表明節點對應的線程因為不滿足一個條件(Condition)而被阻塞。
    • 0: 正常狀態,新生的非CONDITION節點都是此狀態。
    • 非負值標識節點不需要被通知(喚醒)。

    volatile Node prev;此節點的前一個節點。節點的waitStatus依賴于前一個節點的狀態。

    volatile Node next;此節點的后一個節點。后一個節點是否被喚醒(uppark())依賴于當前節點是否被釋放。

    volatile Thread thread;節點綁定的線程。

    Node nextWaiter;下一個等待條件(Condition)的節點,由于Condition是獨占模式,因此這里有一個簡單的隊列來描述Condition上的線程節點。

     

    AQS 在J.U.C里面是一個非常核心的工具,而且也非常復雜,里面考慮到了非常多的邏輯實現,所以在后面的章節中總是不斷的嘗試介紹AQS的特性和實現。

    這一個小節主要介紹了一些理論背景和相關的數據結構,在下一個小節中將根據以上知識來了解Lock.lock/unlock是如何實現的。

     

    參考資料:

    (1)ReentrantLock代碼剖析之ReentrantLock.lock ReentrantLock代碼剖析之ReentrantLock.unlock ReentrantLock代碼剖析之ReentrantLock.lockInterruptibly

    (2)java多線程--java.util.concurrent.locks.AbstractQueuedSynchronizer解析(只包含多線程同步示例)

    (3)處理 InterruptedException

    (4)AbstractQueuedSynchronizer源碼解析之ReentrantLock(一)  AbstractQueuedSynchronizer源碼解析之ReentrantLock(二)

    (5)The java.util.concurrent Synchronizer Framework

     

     



    ©2009-2014 IMXYLZ |求賢若渴
    posted on 2010-07-06 18:29 imxylz 閱讀(53043) 評論(3)  編輯  收藏 所屬分類: J2EE

    評論

    # re: 深入淺出 Java Concurrency (7): 鎖機制 part 2 AQS 2011-05-05 11:20 王海龍
    節點操作因為超時或者對應的線程被interrupt。節點不應該不留在此狀態,一旦達到此狀態將從CHL隊列中踢出。

    應該是

    節點操作因為超時或者對應的線程被interrupt。節點應該不留在此狀態,一旦達到此狀態將從CHL隊列中踢出。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (7): 鎖機制 part 2 AQS 2012-02-23 09:48 xylz
    @王海龍
    是的,已經修正了。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (7): 鎖機制 part 2 AQS 2013-03-12 12:02 aisun
    有序隊列
    在AQS中采用CHL列表來解決有序的隊列的問題。

    這個指的應該是自旋鎖Craig, Landin, and Hagersten (CLH) locks 縮寫是打錯了吧  回復  更多評論
      


    ©2009-2014 IMXYLZ
    主站蜘蛛池模板: 一级中文字幕乱码免费| 亚洲AV无码AV吞精久久| 国产情侣久久久久aⅴ免费| 亚洲自偷自偷偷色无码中文| www在线观看播放免费视频日本| 免费二级毛片免费完整视频| 在线播放国产不卡免费视频| 亚洲人成电影网站国产精品| 国产成人精品免费视频大全| 在线观看亚洲av每日更新| a国产成人免费视频| 久久精品国产亚洲av麻豆小说| 亚洲成人免费网址| 色偷偷亚洲女人天堂观看欧| 午夜影视在线免费观看| 成人免费无码视频在线网站| 亚洲一久久久久久久久| 亚洲A∨午夜成人片精品网站| 永久免费精品影视网站| 久热综合在线亚洲精品| 4444www免费看| 亚洲AV无码AV吞精久久| 亚洲色婷婷综合久久| 亚洲一区二区三区免费在线观看| 亚洲日本va在线观看| 亚洲av再在线观看| 日韩精品免费在线视频| 亚洲va在线va天堂va手机| 国产成人免费a在线资源| 香蕉免费看一区二区三区| 亚洲欧洲视频在线观看| 国产99视频精品免费视频7| 中文字幕无码毛片免费看| 亚洲va在线va天堂va手机| 亚洲欧洲精品成人久久曰影片| 亚洲视频在线观看免费| 亚洲成在人线在线播放无码| 亚洲精品高清无码视频| 免费看韩国黄a片在线观看| 中国一级特黄高清免费的大片中国一级黄色片| 亚洲伊人久久大香线蕉苏妲己|