
上一章你應該掌握了Atomic的底層原理-CAS,接下來進入另一個重要的一個知識AQS,我們通過ReentrantLock這個類來講講AQS這個知識,

從上圖可以看出,ReentractLock、ReadWriteReentractLock,這些鎖API底層是基于AQS+CAS+volatile來實作的,一般不會直接使用,常使用的是一些并發集合API,但是它們的底層大多還是基于ReentrantLock或者AQS來實作的,
ReentrantLock屬于java并發包里的底層的API,專門支撐各種java并發類的底層的邏輯實作,
ReenranctLock的內容比較多,計劃分6節來講,
-
第一節講一下初識ReenranctLock加鎖的AQS底層原理
-
第二節講一下ReenranctLock加鎖入隊的AQS底層原理
-
第三節講一下ReenranctLock釋放鎖的底層原理
-
第四節講一下ReenranctLock鎖的可重入、公平、非公平
-
第五節講一下ReentrantReadWriteLock讀寫鎖的原理
-
第六節講一下ReenranctLock中condition的應用
Hello ReentrantLock
Hello ReentrantLock
很多人可能沒有用過ReentrantLock,在一些并發情況下因為要保證一些原子性操作,可能也會用到,但是大多數人很少接觸高并發的場景,所以用這個類的人可能很少,
但是在一些開源專案中還是有使用到的,比如Spring Cloud的Eureka組件,有時候面試也經常考AQS或者并發集合的問題,所以掌握ReentrantLock的原理是非常有必要的,這樣你可以駕輕就熟的理解它在開源專案的使用,更不會在面試的時候被問住,
第一點還是先來看個HelloWorld的例子,我們為了保證某些操作同一時間只能有一個執行緒操作,會對整個操作加一個鎖,除了synchronized之外,我們還可以使用ReentrantLock,假設有一個操作是j++,
那么Hello ReentrantLock代碼如下:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
static int j = 1;
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
for(int i=0;i<10;i++){
new Thread(()->{
reentrantLock.lock();
try{
System.out.println(Thread.currentThread().getName()+"-結果:"+j++);
}catch (Exception e){
}finally {
reentrantLock.unlock();
}
}).start();
}
}
}
從一張圖先鳥瞰下ReentrantLock核心的3個小組件
從一張圖先鳥瞰下ReentrantLock核心的3個小組件
ReentrantLock核心有3個組件:state、owner、AQS(抽象佇列同步器,簡單的說就是一個等待佇列Queue),如下圖所示:

這里你可以先有個概念就行,你之后會詳細的了解到這幾個組件作用的,
從JDK原始碼層面找一下對應的組件
從JDK原始碼層面找一下對應的組件
當你有了上面這張圖的印象后,我們通過Hello ReentrantLock來分析下它的組件在原始碼里的體現,
首先還是簡單看下ReentrantLock的原始碼脈絡:

它主要脈絡有:
1) 一些API方法
2) 3個內部類,Sync、NonfairSync、Sync
3) 1個成員變數Sync物件
了解了原始碼的大體脈絡后,接下來,分析下它的使用程序,首先肯定是創建ReentrantLock,讓我們來看看建構式做了些什么,代碼如下:
public ReentrantLock() {
sync = new NonfairSync();
}
發現內部創建了物件,賦值給了sync變數,創建了一個內部類NonfairSync,繼續深入可以發現如下代碼關系:
static final class NonfairSync extends Sync {
}
abstract static class Sync extends AbstractQueuedSynchronizer {
原來sync變數創建的物件是NonfairSync,它的父類是Sync,而這個類的父類是一個AbstractQueuedSynchronizer,
你可以猜想下,AbstractQueuedSynchronizer這個是什么東西?從名字上看叫做抽象佇列同步器,縮寫是AQS,咿?這個就是之前提到的AQS啊,
仔細看一下這個父類的脈絡:

首先也是一對方法,但是變數很有意思,UnSafe類、head/tail+Node內部類?這讓你想到了什么?
沒錯,上一節剛接觸過的Aotmic類Unsafe可以用作CAS操作的類,head/tail+Node內部類這不是LinkedList的資料結構么?這個就是上面提到過ReentrantLock的3個小組件之一——等待佇列Queue,
等等,還有一個int state,這個就是上面提到過ReentrantLock的3個小組件之一——state變數,你可以看到這幾個變數對應的代碼如下:
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
state和 等待佇列都看到了,owner去哪里了?原來AQS還有一個父類,叫做AbstractOwnableSynchronizer,
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
}
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
protected AbstractOwnableSynchronizer() { }
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
上面這個exclusiveOwnerThread不就是獨占的owner執行緒的意思么?原來在這里,這就是3個小組件中的最后一個組件-owner****執行緒,
到這里你就可以得到如下所示的原始碼層面的組件圖:

從JDK原始碼層面理解AQS的執行緒第一次加鎖
從JDK原始碼層面理解AQS的執行緒第一次加鎖
當你有了ReentrantLock加鎖程序的這個概念后,來分析下原始碼就很簡單多了,
lock方法原始碼如下:
public void lock() {
sync.lock();
}
直接呼叫了ReentrantLock的內部類,Sync組件的lock方法,而Sync組件lock方法是抽象的,如下:
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
}
sync這變數,在之前的建構式中,實際創建的是AbstractQueuedSynchronizer(AQS)的子類:NonfairSync,所以找到對應的lock方法代碼如下:
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
首先進行的操作就是一個CAS,更新了volatile變數state,由0變為1,底層使用的是Unsafe類操作的,這個和Aotmic類的底層CAS沒什么區別,是類似的,
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
之后如果沒有別的執行緒并發進行CAS操作的話,這個修改state的CAS操作會成功,并且回傳true,接著就會執行setExclusiveOwnerThread方法了,這個方法代碼如下:
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
實際就是設定了當前加鎖的執行緒owner,接著整個lock方法就結束了,
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
最終你會得到如下所示的流程圖:

小結&思考
小結&思考
其實通過這節,你可以發現,其實ReentrantLock就是基于3個屬性來實作的,只不過是通過抽象類封裝了公共的屬性和操作,而這個抽象類常被我們成為AQS,
第一次加鎖其實主要就是
1、CAS操作一個volatile的int state從0->1
2、是指一個onwerThread為加鎖執行緒
3、如果沒有競爭的情況,和Queue佇列沒關系的,
大家學完一個技術后,一定要一會思考和提煉關鍵點、抽象思想,這個是非常重要的!
本文由博客群發一文多發等運營工具平臺 OpenWrite 發布
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/337900.html
標籤:其他
上一篇:低開銷獲取時間戳
