解釋
AQS:全稱“AbstractQueuedSynchronizer”,直譯過來是抽象的隊列同步器,一般我們把它叫做AQS,java中大部分并發類都是通過它來實現線程同步。它內部定義了一個變量(volatile int state)和一個等待隊列,前者表示加鎖狀態,后者在多線程情況下爭用資源時被阻塞會進入等待隊列。
源碼解析
volatile int state
volatile 是一個關鍵字,在并發處理中也經常會用到,后面單獨用一篇文章介紹。
(已去除源碼中的注釋,方便閱讀)
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
獨占鎖(下面會介紹)state初始為0,表示未鎖定狀態,A線程加鎖成功則state+1,這樣后面的線程嘗試獲取鎖的時候就會失敗,只有當A線程釋放鎖state=0時,后面的鎖才有可能成功獲取鎖。如果是可重入鎖,那么A線程再次獲取鎖的時候,state會累加,當然,釋放鎖也要一層一層釋放,直到state=0。共享鎖state初始為N,多個線程可以同時執行,每個線程執行完會state-1,直到state=0時,再調用主線程。
其中compareAndSetState方法,是原子操作,里面使用了unsafe中的compareAndSwapInt方法,這個Unsafe(不安全)類,聽著就不靠譜,為什么這里會用到呢。其實不止在這里,在JUC(java.util.concurrent)包中,尤其是在CAS里大量的用到Unsafe,這是因為Java不能像c語言那樣直接訪問操作系統底層,但Unsafe類提供了硬件級別的原子操作,而且有很高的效率。那為什么官方不建議開發者使用這個類呢,感覺特地取個這名字(Unsafe)就是嚇唬程序員,不要用。因為Unsafe中直接訪問內存的方法中使用的內存不受JVM管理,也就不能被GC,需要手動管理,稍有不慎就可能導致內存泄漏。
隊列操作
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
將節點插入隊列,必要時進行初始化。
里面的for (;;),又是個騷操作,有人會問為啥不用while(true)呢,都是無限循環,而且while(true)看著就很舒服。經??丛创a的同學,可能會發現,jdk源碼中很多地方都是for(;;),至于為啥用這個,咱們以后有機會再討論。
Node t = tail;(tail是尾節點,head是頭節點)先定義一個尾部節點,如果尾節點是空的,說明隊列是空的,這時就需要初始化,源碼中特地加了注釋// Must initialize,(這種直接在后面加注釋的,咱們最好不要學,不符合阿里代碼規范,但是Doug Lea是大大佬,阿里代碼規范管不住他);初始化以后,接著循環進入else,node.prev = t;將當前節點掛在尾節點后面;(這種node.prev指向上個節點,t.next指向下個節點,這是雙向隊列的操作,還有單向隊列,有興趣的可以去了解一下)。
看這一個方法里就出現了compareAndSetHead和compareAndSetTail兩個CAS操作,點進去發現還是Unsafe實現的,由此可見這個類在多線程中還是很重要的。
AQS中還有很多方法,咱們不一一介紹了,下面介紹幾個和資源共享相關的方法。
資源共享方式
AQS定義了兩種資源共享方式
- 獨占資源,只有一個線程能執行,如ReentrantLock
- 共享資源,多個線程可以同時執行,如Semaphore/CountDownLatch。
獨占模式采用tryAcquire-tryRelease實現,共享模式采用tryAcquireShared-tryReleaseShared實現。有一些特殊的鎖兩種模式都使用,比如ReentrantReadWriteLock(讀寫鎖)。
-
isHeldExclusively():該線程是否獨占資源。
protected boolean isHeldExclusively() { throw new UnsupportedOperationException(); }
-
tryAcquire(int):獨占方式。嘗試獲取資源。
protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
-
tryRelease(int):獨占方式。嘗試釋放資源。
protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); }
-
tryAcquireShared(int):共享方式。嘗試獲取資源。負數表示失敗; 0 表示成功,但沒有剩余
可用資源;正數表示成功,且有剩余資源。protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException(); }
-
tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放后允許喚醒后續等待結點返回
true,否則返回 false。protected boolean tryReleaseShared(int arg) { throw new UnsupportedOperationException(); }