AbstractQueuedSynchronizer
隊列同步器,簡稱AQS,是一個用來構建鎖或者其他同步組件的基礎框架,他使用了一個int型的成員變量來表示同步狀態,內部通過FIFO隊列來完成資源獲取線程的排隊工作
AQS使用方式和其中的設計模式
AQS的主要使用方式是繼承,子類通過繼承AQS並實現它的抽象方法來管理同步狀態,在AQS裏由一個int型的state來代表這個狀態,在抽象方法的實現過程中免不了要對同步狀態進行更改,這時就需要使用同步器提供的3個方法(getState()、setState(int newState)和compareAndSetState(int expect,int update))來進行操作,因爲它們能夠保證狀態的改變是安全的。
private volatile int state;
在實現上,子類推薦被定義爲自定義同步組件的靜態內部類,AQS自身沒有實現任何同步接口,它僅僅是定義了若干同步狀態獲取和釋放的方法來供自定義同步組件使用,同步器既可以支持獨佔式地獲取同步狀態,也可以支持共享式地獲取同步狀態,這樣就可以方便實現不同類型的同步組件(ReentrantLock、ReentrantReadWriteLock和CountDownLatch等)。
同步器是實現鎖(也可以是任意同步組件)的關鍵,在鎖的實現中聚合同步器。可以這樣理解二者之間的關係:
鎖是面向使用者的,它定義了使用者與鎖交互的接口(比如可以允許兩個線程並行訪問),隱藏了實現細節;
同步器面向的是鎖的實現者,它簡化了鎖的實現方式,屏蔽了同步狀態管理、線程的排隊、等待與喚醒等底層操作。鎖和同步器很好地隔離了使用者和實現者所需關注的領域。
實現者需要繼承同步器並重寫指定的方法,隨後將同步器組合在自定義同步組件的實現中,並調用同步器提供的模板方法,而這些模板方法將會調用使用者重寫的方法。
AQS基於CLH隊列鎖實現
CLH隊列鎖即Craig, Landin, and Hagersten (CLH) locks。
CLH隊列鎖也是一種基於鏈表的可擴展、高性能、公平的自旋鎖,申請線程僅僅在本地變量上自旋,它不斷輪詢前驅的狀態,假設發現前驅釋放了鎖就結束自旋。
當一個線程需要獲取鎖時:
1.創建一個新的QNode,將其中的locked設置爲true表示需要獲取鎖,myPred表示對其前驅結點的引用
2.線程A對tail域調用getAndSet方法,使自己成爲隊列的尾部,同時獲取一個指向其前驅結點的引用myPred
3.線程B需要獲得鎖,同樣的流程再來一遍
4.線程就在前驅結點的locked字段上旋轉,直到前驅結點釋放鎖(前驅節點的鎖值 locked == false)
5.當一個線程需要釋放鎖時,將當前結點的locked域設置爲false,同時回收前驅結點
如上圖所示,前驅結點釋放鎖,線程A的myPred所指向的前驅結點的locked字段變爲false,線程A就可以獲取到鎖。
CLH隊列鎖的優點是空間複雜度低(如果有n個線程,L個鎖,每個線程每次只獲取一個鎖,那麼需要的存儲空間是O(L+n),n個線程有n個myNode,L個鎖有L個tail)。CLH隊列鎖常用在SMP體系結構下。
Java中的AQS是CLH隊列鎖的一種變體實現。
AQS中的方法
acquire():獨佔式獲取同步狀態,如果當前線程獲取同步狀態成功,則由該方法返回,否則將會進入同步隊列等待,該方法將會調用重寫的tryAcquire(int arg)方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquireInterruptibly:與acquire相同,但是該方法會響應中斷,當線程未獲取到同步狀態而進入同步隊列中,如果當前線程被中斷,則該方法會拋出InterruptException並返回
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
tryAcquireNanos:在acquireInterruptibly基礎上增加了超時限制,如果當前線程在超時時間內沒有互毆去到同步鎖狀態,那麼就會返回false,如果獲取到了就返回true
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
acquireShared:共享式的獲取同步狀態,如果當前線程爲獲取到同步狀態,將會進入同步隊列等待,與獨佔式獲取的主要區別是在同一時刻可以有過個線程獲取到同步狀態
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
acquireSharedInterruptibly:與acquireShared相同,該方法會響應中斷
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireSharedNanos:在acquireSharedInterruptibly基礎上增加了超時限制
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
release:獨佔式的釋放同步狀態,該方法會在釋放同步狀態之後,將同步隊列中第一個節點包含的線程喚醒
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
releaseShared:共享式的釋放同步狀態
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
getQueuedThreads:獲取等待在同步隊列上的線程集合
public final Collection<Thread> getQueuedThreads() {
ArrayList<Thread> list = new ArrayList<Thread>();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
list.add(t);
}
return list;
}
其中可以被重寫的方法:
tryAcquire
tryRelease
tryAcquireShared
tryReleaseShared
isHeldExclusively
既然AQS是實現同步器的基礎框架,那麼我們怎麼用他來實現一個我們自己的類似ReentrantLock的工具呢?
如下,定義一個類實現Lock接口(每個鎖都要實現的接口),然後定義一個內部類,我們這裏命名爲MySync繼承自AbstractQueuedSynchronizer,然後我們自定義的鎖的所有操作都是交給這個內部類MySync實現的
public class MyLock implements Lock {
MySync sync = new MySync();
@Override
public void lock() {
System.out.println(Thread.currentThread().getName()+" ready get lock");
sync.acquire(1);
System.out.println(Thread.currentThread().getName()+" already got lock");
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(time));
}
@Override
public void unlock() {
System.out.println(Thread.currentThread().getName()+" ready release lock");
sync.release(1);
System.out.println(Thread.currentThread().getName()+" already released lock");
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
static class MySync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0){
throw new IllegalMonitorStateException("already release lock");
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
protected Condition newCondition(){
return new ConditionObject();
}
}
}
測試類
public class TestMyLock {
public void test() {
final Lock lock = new MyLock();
for (int i = 0; i < 4; i++) {
Worker w = new Worker(lock);
w.start();
}
}
public static void main(String[] args) {
TestMyLock lock = new TestMyLock();
lock.test();
}
class Worker extends Thread {
private final Lock lock;
public Worker(Lock lock) {
this.lock = lock;
}
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getName());
try {
SleepTools.second(3);
} finally {
lock.unlock();
}
}
}
}
打印:
Thread-0 ready get lock
Thread-0 already got lock
Thread-0
Thread-1 ready get lock
Thread-2 ready get lock
Thread-3 ready get lock
Thread-0 ready release lock
Thread-0 already released lock
Thread-1 already got lock
Thread-1
Thread-1 ready release lock
Thread-1 already released lock
Thread-2 already got lock
Thread-2
Thread-2 ready release lock
Thread-2 already released lock
Thread-3 already got lock
Thread-3
Thread-3 ready release lock
Thread-3 already released lock
Process finished with exit code 0
我們這個鎖有個缺陷,他目前是不可重入的,接下來我們實現一個自己的可重入鎖。重入鎖和非重入鎖的差別其實並不大,關鍵就在於獲取鎖和釋放鎖的時候是否有判斷當前線程是否是已經持有鎖的線程這一步
public class MyReentrantLock implements Lock {
MyReentrantSync reentrantSync = new MyReentrantSync();
@Override
public void lock() {
reentrantSync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
reentrantSync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return reentrantSync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return reentrantSync.tryAcquireNanos(1,unit.toNanos(time));
}
@Override
public void unlock() {
reentrantSync.release(1);
}
@Override
public Condition newCondition() {
return reentrantSync.newCondition();
}
class MyReentrantSync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
if (Thread.currentThread() == getExclusiveOwnerThread()){
setState(getState()+1);
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (Thread.currentThread() != getExclusiveOwnerThread()){
throw new IllegalMonitorStateException();
}
if (getState() == 0){
throw new IllegalMonitorStateException("already release");
}
setState(getState()-1);
if (getState() == 0){
setExclusiveOwnerThread(null);
}
return true;
}
protected Condition newCondition(){
return new ConditionObject();
}
}
}
測試類
public class TestReenterSelfLock {
static final Lock lock = new MyReentrantLock();
public void reenter(int x){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+":遞歸層級:"+x);
int y = x - 1;
if (y==0) return;
else{
reenter(y);
}
} finally {
lock.unlock();
}
}
public void test() {
for (int i = 0; i < 3; i++) {
Worker w = new Worker();
w.start();
}
}
public static void main(String[] args) {
TestReenterSelfLock testMyLock = new TestReenterSelfLock();
testMyLock.test();
}
class Worker extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
reenter(3);
}
}
}
打印結果
Thread-0
Thread-1
Thread-2
Thread-0:遞歸層級:3
Thread-0:遞歸層級:2
Thread-0:遞歸層級:1
Thread-2:遞歸層級:3
Thread-2:遞歸層級:2
Thread-2:遞歸層級:1
Thread-1:遞歸層級:3
Thread-1:遞歸層級:2
Thread-1:遞歸層級:1