AbstractQueuedSynchronizer即抽象隊列同步器,AQS定義了一套多線程訪問共享資源的同步器框架,許多同步類實現(xiàn)都依賴于它,如常用的ReentrantLock/Semaphore/CountDownLatch。 它維護了一個volatile int state(代表共享資源)和一個FIFO雙端隊列(多線程爭用阻塞時線程進入此隊列尾部,隊列頭節(jié)點是成功獲取鎖的線程,當頭節(jié)點釋放鎖時,會喚醒后面節(jié)點并釋放當前頭節(jié)點的引用)。 AQS定義兩種資源共享方式:Exclusive(獨占,只有一個線程能執(zhí)行,如ReentrantLock)和Share(共享,多個線程可同時執(zhí)行,如Semaphore/CountDownLatch)。 不同自定義同步器爭用共享資源的方式也不同。自定義同步器在實現(xiàn)時只需要實現(xiàn)共享資源state的獲取與釋放方式即可,至于具體線程等待隊列的維護(如獲取資源失敗入隊/喚醒出隊等),AQS已經在頂層實現(xiàn)好了。
獨占鎖的獲取流程 (1)調用入口方法acquire(arg) (2)調用模版方法tryAcquire(arg)嘗試獲取鎖,若成功則返回,若失敗則走下一步 (3)將當前線程構造成一個Node節(jié)點,并利用CAS將其加入到同步隊列尾部,然后該節(jié)點對應線程進入自旋狀態(tài) (4)自旋時首先判斷其前驅節(jié)點釋放為頭節(jié)點&是否成功獲取同步狀態(tài),兩個條件都成立,則將當前線程的節(jié)點設置為頭節(jié)點,如果不是,則利用LockSupport.park(this)將當前線程掛起 ,等待被前驅節(jié)點喚醒
獨占鎖的釋放流程 (1)調用入口方法release(arg) (2)調用模版方法tryRelease(arg)釋放同步狀態(tài) (3)獲取同步隊列中當前節(jié)點的下一節(jié)點 (4)利用LockSupport.unpark(currentNode.next.thread)喚醒后繼節(jié)點
共享鎖的獲取流程 (1)調用acquireShared(arg)入口方法 (2)進入tryAcquireShared(arg)方法獲取同步狀態(tài),如果返回值>=0,說明同步狀態(tài)(state)有剩余,獲取鎖成功直接返回 如果返回值<0,說明獲取同步狀態(tài)失敗,向隊列尾部添加一個共享類型的Node節(jié)點,隨即該節(jié)點進入自旋狀態(tài) (3)自旋時,首先檢查前驅節(jié)點釋放為頭節(jié)點&tryAcquireShared()是否>=0(即成功獲取同步狀態(tài))。如果是則說明當前節(jié)點可執(zhí)行,把當前節(jié)點設置為頭節(jié)點并喚醒所有后繼節(jié)點;如果否,則利用LockSupport.unpark(this)掛起當前線程,等待被前驅節(jié)點喚醒
共享鎖的釋放流程 重入鎖 非公平鎖與公平鎖 非公平鎖是指當鎖狀態(tài)為可用時,不管在當前鎖上是否有其他線程在等待,新近線程都有機會搶占鎖。 公平鎖是指當多個線程嘗試獲取鎖時,成功獲取鎖的順序與請求獲取鎖的順序相同。 AQS實現(xiàn)中兩者區(qū)別在于是否判斷當前節(jié)點存在前驅節(jié)點!hasQueuedPredecessors() &&,如果當前線程獲取鎖失敗就會被加入到AQS同步隊列,那么如果同步隊列中的節(jié)點存在前驅節(jié)點,也就表明存在線程比當前節(jié)點線程更早獲取鎖,只有等待前面線程釋放鎖后才能獲取鎖。
讀寫鎖 基于AQS的讀寫鎖實現(xiàn)ReentrantReadWriteLock,該讀寫鎖實現(xiàn)原理是:將同步變量state按照高16位和低16位拆分,高16位表示讀鎖,低16位表示寫鎖。 寫鎖的獲取 (1)獲取同步狀態(tài),從中分離出低16位的寫鎖狀態(tài) (2)如果同步狀態(tài)不為0,說明存在讀鎖或寫鎖 (3)如果存在讀鎖(c !=0 && w == 0),則不能獲取寫鎖(保證寫對讀的可見性) (4)如果當前線程不是上次獲取寫鎖的線程,則不能獲取寫鎖(寫鎖為獨占鎖) (5)如果以上判斷均通過,則在低16位同步狀態(tài)上利用CAS進行修改(增加寫鎖同步狀態(tài),實現(xiàn)可重入) (6)將當前線程設置為寫鎖的獲取線程 寫鎖的釋放與獨占鎖類似,不斷減少讀鎖同步狀態(tài),同步狀態(tài)為0時,寫鎖完全釋放
讀鎖(共享鎖)的獲取 (1)獲取當前同步狀態(tài) (2)計算高16為讀鎖狀態(tài) 1后的值 (3)如果大于能夠獲取到的讀鎖的最大值,則拋出異常 (4)如果存在寫鎖并且當前線程不是寫鎖的獲取者,則獲取讀鎖失敗 (5)如果上述判斷都通過,則利用CAS重新設置讀鎖的同步狀態(tài) 來源:http://www./content-4-120601.html
|