初识Lock与AbstractQueuedSynchronizer(AQS)
本文轉載于:https://juejin.im/post/5aeb055b6fb9a07abf725c8c
一、concurrent包的結構層次
在針對并發編程中,Doug Lea大師為我們提供了大量實用,高性能的工具類,針對這些代碼進行研究會讓我們隊并發編程的掌握更加透徹也會大大提升我們隊并發編程技術的熱愛。這些代碼在java.util.concurrent包下。如下圖,即為concurrent包的目錄結構圖。
其中包含了兩個子包:atomic以及lock,另外在concurrent下的阻塞隊列以及executors,這些就是concurrent包中的精華,之后會一一進行學習。而這些類的實現主要是依賴于volatile以及CAS,從整體上來看concurrent包的整體實現圖如下圖所示:
二、lock簡介
我們下來看concurent包下的lock子包。鎖是用來控制多個線程訪問共享資源的方式,一般來說,一個鎖能夠防止多個線程同時訪問共享資源。在Lock接口出現之前,java程序主要是靠synchronized關鍵字實現鎖功能的,而java SE5之后,并發包中增加了lock接口,它提供了與synchronized一樣的鎖功能。雖然它失去了像synchronize關鍵字隱式加鎖解鎖的便捷性,但是卻擁有了鎖獲取和釋放的可操作性,可中斷的獲取鎖以及超時獲取鎖等多種synchronized關鍵字所不具備的同步特性。通常使用顯式使用lock的形式如下:
Lock lock = new ReentrantLock(); lock.lock(); try{....... }finally{lock.unlock(); }需要注意的是synchronized同步塊執行完成或者遇到異常是鎖會自動釋放,而lock必須調用unlock()方法釋放鎖,因此在finally塊中釋放鎖。
Lock接口定義的方法:
從最熟悉的ReentrantLock說起
很顯然ReentrantLock實現了lock接口,接下來我們來仔細研究一下它是怎樣實現的。當你查看源碼時你會驚訝的發現ReentrantLock并沒有多少代碼,另外有一個很明顯的特點是:基本上所有的方法的實現實際上都是調用了其靜態內存類Sync中的方法,而Sync類繼承了AbstractQueuedSynchronizer(AQS)。可以看出要想理解ReentrantLock關鍵核心在于對隊列同步器AbstractQueuedSynchronizer(簡稱同步器)的理解。
三、初識AQS
同步器是用來構建鎖和其他同步組件的基礎框架,它的實現主要依賴一個int成員變量來表示同步狀態以及通過一個FIFO隊列構成等待隊列。它的子類必須重寫AQS的幾個protected修飾的用來改變同步狀態的方法,其他方法主要是實現了排隊和阻塞機制。狀態的更新使用getState,setState以及compareAndSetState這三個方法。
子類被推薦定義為自定義同步組件的靜態內部類,同步器自身沒有實現任何同步接口,它僅僅是定義了若干同步狀態的獲取和釋放方法來供自定義同步組件的使用,同步器既支持獨占式獲取同步狀態,也可以支持共享式獲取同步狀態,這樣就可以方便的實現不同類型的同步組件。
同步器是實現鎖(也可以是任意同步組件)的關鍵,在鎖的實現中聚合同步器,利用同步器實現鎖的語義。可以這樣理解二者的關系:鎖是面向使用者,它定義了使用者與鎖交互的接口,隱藏了實現細節;同步器是面向鎖的實現者,它簡化了鎖的實現方式,屏蔽了同步狀態的管理,線程的排隊,等待和喚醒等底層操作。鎖和同步器很好的隔離了使用者和實現者所需關注的領域。
AQS的設計是使用模板方法設計模式,它將一些方法開放給子類進行重寫,而同步器給同步組件所提供模板方法又會重新調用被子類所重寫的方法。舉個例子,AQS中需要重寫的方法tryAcquire:
ReentrantLock中NonfairSync(繼承AQS)會重寫該方法為:
protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires); }而AQS中的模板方法acquire():
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}會調用tryAcquire方法,而此時當繼承AQS的NonfairSync調用模板方法acquire時就會調用已經被NonfairSync重寫的tryAcquire方法。這就是使用AQS的方式,在弄懂這點后會lock的實現理解有很大的提升。可以歸納總結為這么幾點:
1.同步組件(這里不僅僅值鎖,還包括CountDownLatch等)的實現依賴于同步器AQS,在同步組件實現中,使用AQS的方式被推薦定義繼承AQS的靜態內存類;
2.AQS采用模板方法進行設計,AQS的protected修飾的方法需要由繼承AQS的子類進行重寫實現,當調用AQS的子類的方法時就會調用被重寫的方法;
3.AQS負責同步狀態的管理,線程的排隊,等待和喚醒這些底層操作,而Lock等同步組件主要專注于實現同步語義;
4.在重寫AQS的方式時,使用AQS提供的getState(),setState(),compareAndSetState()方法進行修改同步狀態
AQS可重寫的方法如下圖(摘自《java并發編程的藝術》一書):
在實現同步組件時AQS提供的模板方法如下圖:
獨占式獲取與釋放同步狀態;
共享式獲取與釋放同步狀態;
查詢同步隊列中等待線程情況;
同步組件通過AQS提供的模板方法實現自己的同步語義。
總結
以上是生活随笔為你收集整理的初识Lock与AbstractQueuedSynchronizer(AQS)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 错过SaaS,就是错过这个时代
- 下一篇: 深入理解AbstractQueuedSy