這篇文章,咱們聊一聊Java併發中的核武器, AQS底層實現。面試
不論是工做三四年、仍是五六年的在工做或者面試中涉及到併發的是時候老是繞不過AQS這個詞。併發
首先,確實還有不少人連AQS是什麼都不知道,甚至有的竟不知其爲什麼物。或者有的據說過其名,但怎麼拼寫的都忘記了。搜索引擎
總的來講確實有不少同窗對AQS總有一種雲裏霧裏的感受,在搜索引擎中搜下AQS看個幾篇文章,估計對其仍是醉醺醺的。spa
因此根據上面的難點,這篇咱們使用由簡入難的方式,讓你一次搞定這Java併發中這個核武器AQS。線程
首先咱們以你最受的方式帶你進入這個核武器庫,Java 併發包下的 ReentrantLock你們確定很熟悉了。code
基本上學過Java 的都知道ReentrantLock,下面我就很少說了直接上一段代碼。對象
ReentrantLock lock = new ReentrantLock(); try { lock.lock(); // 加鎖 // 業務邏輯代碼 } finally { lock.unlock(); // 釋放鎖 }
這段代碼你們應該很熟悉了,無非就是獲取一把鎖,加鎖和釋放鎖的過程。索引
有同窗就問了這和AQS有毛關係呀!彆着急,告訴你關係大着去了。在Java併發包中不少鎖都是經過AQS來實現加鎖和釋放鎖的過程的,AQS就是併發包基礎。隊列
例如:ReentrantLock、ReentrantReadWriteLock 底層都是經過AQS來實現的。圖片
那麼AQS到底爲什麼物尼?別急,咱們一步一來揭開其神祕的面紗。
AQS 的全稱 AbstractQueuedSynchronizers抽象隊列同步器,給你們畫三張圖來講明其在Java 併發包的地位、 長啥樣、和ReentrantLock 的關係。
經過此類圖能夠彰顯出了AQS的地位、上層鎖實現基本都是經過其底層來實現的。
有沒有被忽悠的感受?你沒看錯AQS就長這個鳥樣。說白了其內部就是包含了三個組件
在看這張圖如今明白ReentrantLock 和 AQS 的關係了吧!大白話說就是ReentrantLock其內部包含一個AQS對象(內部類),AQS就是ReentrantLock能夠獲取和釋放鎖實現的核心部件。
好了! 通過上面的介紹估計你們已經對AQS混了個臉熟,下面咱們就來講說這一段代碼。
ReentrantLock lock = new ReentrantLock(); try { lock.lock(); // 加鎖 // 業務邏輯代碼 } finally { lock.unlock(); // 釋放鎖 }
這段代碼加鎖和釋放鎖到底會發生什麼故事尼?
很簡單在AQS 內部有一個核心變量 (volatile)state 變量其表明了加鎖的狀態,初始值爲0。
另一個重要的關鍵 OwnerThread 持有鎖的線程,默認值爲null 在回顧下這張圖。
接着線程1過來經過lock.lock()方式獲取鎖,獲取鎖的過程就是經過CAS操做volatile 變量state 將其值從0變爲1。
若是以前沒有人獲取鎖,那麼state的值確定爲0,此時線程1加鎖成功將state = 1。
線程1加鎖成功後還有一步重要的操做,就是將OwnerThread 設置成爲本身。以下圖線程1加鎖過程。
其實到這你們應該對AQS有個大概認識了,說白了就是併發包下面的一個核心組件,其內部維持state變量、線程變量等核型的東西,來實現加鎖和釋放鎖的過程。
你們有沒有不論是ReentrantLock仍是ReentrantReadWriteLock 等爲何都是Reentrant 開頭尼?
從單詞自己意思也能看出,Reentrant 可重入的意思 ,也就說其是一個可重入鎖。
可重入鎖?
就是你能夠對一個 ReentrantLock 進行屢次的lock() 和 unlock() 操做,也就是能夠對一個鎖加屢次,叫作可重入鎖。 來一段代碼直觀感覺下。
ReentrantLock lock = new ReentrantLock(); try { lock.lock(); // 加鎖1 // 業務邏輯代碼 lock.lock() // 加鎖2 // 業務邏輯代碼 lock.lock() // 加鎖3 } finally { lock.unlock(); // 釋放鎖3 lock.unlock(); // 釋放鎖2 lock.unlock(); // 釋放鎖1 }
注意:釋放鎖是由內到外依次釋放的,不可缺乏。
問題又來了?ReentrantLock 內部又是如何來實現的尼?
說白了!仍是咱們AQS這個核心組件幫我實現的,很 easy~ 上述兩個核心變量 state 和 OwnerThread 還記得吧!
重入就是判斷當前鎖是否是本身加上的,若是是就表明本身能夠在次上鎖,每重入一次就是將state值加1。就是這麼簡單啦!!!
說完了可重入咱們再來看看鎖的互斥又是如何實現的尼?
此時線程2也跑過來想加鎖,CAS操做嘗試將 state 從0 變成 1, 哎呀!糟糕state已經不是0了,說明此鎖已經被別人拿到了。
接着線程2想??? 這個鎖是否是我之前加上的,瞅瞅 OwnerThread=線程1 哎! 明顯不是本身上的 ,悲催加鎖失敗了~~~。來張圖記錄下線程2的悲苦經歷。
但是線程2加鎖失敗將何去何從尼?
線程2:想,要是有個地方讓我休息下,等線程1釋放鎖後通知我下再來重新嘗試上鎖就行了。
這時咱們的核心部件AQS又登場了!
AQS: OK! 好吧!那我就給你提供一個落腳地吧(CLH)進去待着吧!一會讓線程1叫你。
線程2: 屁顛屁顛的就去等待區小憩一會去了。一樣來張圖記錄下線程2高興樣。
此時線程1業務執行完了,開始釋放鎖
線程2一聽,樂壞了!立馬開始嘗試獲取取鎖,CAS 嘗試將 state 值設爲 1 ,若是成功將OwnerThread設爲本身 線程2。
此時線程2成功獲取到了鎖,再來張圖瞅瞅。
Ok !到這藉着Reentrantkock 的加鎖和釋放鎖的過程給你們講解了一下AQS工做原理。
用一句話總結下:AQS就是Java併發包下的一個基礎組件,用來實現各類鎖和同步組件的,其核心分爲三個組件。
等併發核心組件。