Java利用AQS實(shí)現(xiàn)自定義鎖
什么是AQS
AQS(AbstractQueuedSynchronizer),中文名抽象隊(duì)列同步器
AQS定義了一套多線程訪問共享資源的同步器框架,主要用來自定義鎖和同步器
AQS原理
AQS 核心思想:
- 如果被請(qǐng)求的共享資源空閑,則將當(dāng)前請(qǐng)求資源的線程設(shè)置為有效的工作線程,并且將共享資源設(shè)置為鎖定狀態(tài)。
- 如果被請(qǐng)求的共享資源被占用,將暫時(shí)獲取不到鎖的線程加入到阻塞隊(duì)列中,等待被喚醒和鎖的分配
實(shí)現(xiàn)核心思想的的隊(duì)列:CLH隊(duì)列
CLH隊(duì)列是一個(gè)虛擬的雙向隊(duì)列,AQS 是將每條請(qǐng)求共享資源的線程封裝成一個(gè) CLH 鎖隊(duì)列的一個(gè)結(jié)點(diǎn)(Node)來實(shí)現(xiàn)鎖的分配。
共享資源用 volatile 關(guān)鍵詞修飾,保證線程間的可見性
/** * The synchronization state. */ private volatile int state;
0狀態(tài)表示空閑,1狀態(tài)或以上表示不空閑
共享資源(state)的訪問方式有三種:
- getState() 獲得共享資源狀態(tài)
- setState() 設(shè)置共享資源狀態(tài)
- compareAndSetState() 更改共享資源狀態(tài)(底層unsafe類)
源代碼如下
/** * Returns the current value of synchronization state. * This operation has memory semantics of a {@code volatile} read. * @return current state value */ protected final int getState() { return state; } /** * Sets the value of synchronization state. * This operation has memory semantics of a {@code volatile} write. * @param newState the new state value */ protected final void setState(int newState) { state = newState; } /** * Atomically sets synchronization state to the given updated * value if the current state value equals the expected value. * This operation has memory semantics of a {@code volatile} read * and write. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that the actual * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
利用AQS實(shí)現(xiàn)自定義鎖
一:首先創(chuàng)建一個(gè)類實(shí)現(xiàn)Lock接口,它有6個(gè)方法需要實(shí)現(xiàn)
- lock():加鎖(不成功進(jìn)入阻塞隊(duì)列等待)
- lockInterruptibly():是否加鎖可打斷
- tryLock()://加鎖(不成功不會(huì)進(jìn)入阻塞隊(duì)列等待,可以去做其他事情)
- tryLock(long time,TimeUnit unit):加鎖(規(guī)定時(shí)間內(nèi)未獲得則放棄加鎖)
- unlock():釋放鎖
- newCondition():創(chuàng)建條件變量
二:創(chuàng)建一個(gè)內(nèi)部類,繼承AbstractQueuedSynchronizer
可以根據(jù)需求重寫具體方法,總共有5種方法
- isHeldExclusively():該線程是否正在獨(dú)占資源。只有用到condition才需要去實(shí)現(xiàn)它。
- tryAcquire(int):獨(dú)占方式。嘗試獲取資源,成功則返回true,失敗則返回false。
- tryRelease(int):獨(dú)占方式。嘗試釋放資源,成功則返回true,失敗則返回false。
- tryAcquireShared(int):共享方式。嘗試獲取資源。負(fù)數(shù)表示失??;0表示成功,但沒有剩余可用資源;正數(shù)表示成功,且有剩余資源。
- tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放后允許喚醒后續(xù)等待結(jié)點(diǎn)返回true,否則返回false。
三:我需要自定義一個(gè)獨(dú)占鎖,不可重入,具有變量條件的鎖
分析
- 獨(dú)占鎖:AQS同步器中需要重寫?yīng)氄挤绞降墨@取資源tryAcquire(int)和釋放資源tryRelease(int)方法
- 不可重入:AQS同步器需要實(shí)現(xiàn)isHeldExclusively():
- 具有條件變量:AQS同步器中 return new ConditionObject();
具體代碼如下
//自定義鎖(不可重入)(獨(dú)占鎖)(條件變量) class MyLock implements Lock{ //內(nèi)部類,AQS同步器類 class MySync extends AbstractQueuedSynchronizer{ @Override protected boolean tryAcquire(int arg) { if (compareAndSetState(0,1)){ System.out.println("獲得鎖成功"); //加上了鎖,并設(shè)置owner為當(dāng)前線程 setExclusiveOwnerThread(Thread.currentThread()); return true; } System.out.println("獲得鎖失敗"); return false; } @Override protected boolean tryRelease(int arg) { setExclusiveOwnerThread(null); setState(0); return true; } @Override protected boolean isHeldExclusively() { return getState() == 1; } public Condition newCondition(){ return new ConditionObject(); } } private MySync mySync = new MySync(); @Override //加鎖(不成功進(jìn)入阻塞隊(duì)列等待) public void lock() { mySync.acquire(1); } @Override //加鎖可打斷 public void lockInterruptibly() throws InterruptedException { mySync.acquireInterruptibly(1); } @Override //加鎖(不成功不會(huì)進(jìn)入阻塞隊(duì)列等待,可以去做其他事情) public boolean tryLock() { return mySync.tryAcquire(1); } @Override //嘗試加鎖 帶時(shí)間 public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return mySync.tryAcquireNanos(1,unit.toNanos(time)); } @Override //釋放鎖 public void unlock() { mySync.release(1); } @Override //創(chuàng)建條件變量 public Condition newCondition() { return mySync.newCondition(); } }
到此這篇關(guān)于Java利用AQS實(shí)現(xiàn)自定義鎖的文章就介紹到這了,更多相關(guān)Java AQS實(shí)現(xiàn)自定義鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java精品項(xiàng)目瑞吉外賣之新增菜品與分頁查詢篇
這篇文章主要為大家詳細(xì)介紹了java精品項(xiàng)目-瑞吉外賣訂餐系統(tǒng),此項(xiàng)目過大,分為多章獨(dú)立講解,本篇內(nèi)容為新增菜品和分頁查詢功能的實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05java實(shí)現(xiàn)二維碼生成的幾個(gè)方法(推薦)
本篇文章主要介紹了java實(shí)現(xiàn)二維碼生成的幾個(gè)方法(推薦),具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12Java并發(fā)Map面試線程安全數(shù)據(jù)結(jié)構(gòu)全面分析
本文將探討如何在Java中有效地應(yīng)對(duì)這些挑戰(zhàn),介紹一種強(qiáng)大的工具并發(fā)Map,它能夠幫助您管理多線程環(huán)境下的共享數(shù)據(jù),確保數(shù)據(jù)的一致性和高性能,深入了解Java中的并發(fā)Map實(shí)現(xiàn),包括ConcurrentHashMap和ConcurrentSkipListMap,及相關(guān)知識(shí)點(diǎn)2023-09-09關(guān)于spring版本與JDK版本不兼容的問題及解決方法
這篇文章主要介紹了關(guān)于spring版本與JDK版本不兼容的問題,本文給大家?guī)砹私鉀Q方法,需要的朋友可以參考下2018-11-11SpringBoot實(shí)現(xiàn)獲取客戶端IP地理位置
在當(dāng)今互聯(lián)的世界中,了解客戶端的地理位置對(duì)于提供個(gè)性化服務(wù)和增強(qiáng)用戶體驗(yàn)至關(guān)重要,使用本文為大家介紹了SpringBoot獲取客戶端IP地理位置的相關(guān)方法,需要的小伙伴可以參考下2023-11-11