怎麼利用AbstractQueuedSynchronizer實現自定義同步元件?

sayWhat_sayHello發表於2019-02-18

實現步驟

1. 確定訪問模式。是共享的還是獨佔的?是否需要公平?

實現內部靜態類(常用類名為Sync)繼承AbstractQueuedSynchronizer。根據訪問模式確定重寫哪種方法:
可重寫的方法:

方法名稱 描述
protected boolean tryAcquire(int arg) 獨佔式獲取同步狀態,實現該方法需要查詢當前狀態並判斷同步狀態是否符合預期,然後再進行CAS設定同步狀態。
protected boolean tryRelease(int arg) 獨佔式釋放同步狀態,等待獲取同步狀態的執行緒將有機會獲取同步狀態。
protected int tryAcquireShared(int arg) 共享式獲取同步狀態,返回>=0的值,表示獲取成功,反之失敗
protected boolean tryReleaseShared(int arg) 共享式釋放同步狀態
protected boolean isHeldExclusively() 當前同步器是否在獨佔模式下被執行緒佔用,一般該方法表示是否被當前執行緒獨佔

2. 定義資源數。是確定的還是引數設定的?

在靜態內部類時一般都是用參數列示資源,在組合時會選擇繼續傳引數還是固定值。資源數也就是狀態數,個人習慣稱為資源數。

3. 組合自定義同步器。是否需要使用模板方法?

這裡比較靈活,有的實現有呼叫有的沒有。實現自定義同步元件時,可以呼叫同步器提供的模板方法:

方法 描述
void acquire(int arg) 獨佔式獲取同步狀態,如果當前執行緒獲取同步狀態成功,則由該方法返回,否則,將會進入同步佇列等待,該方法會呼叫重寫的tryAcquire(int arg)方法
void acquireInterruptibly(int arg) 與acquire(int arg)相同,但該方法響應中斷,當前執行緒未獲取到同步狀態而進入同步佇列中,如果當前執行緒被中斷,則該方法會丟擲InterruptedException並返回
boolean tryAcquireNanos(int arg,long nanos) 在acquireInterruptibly(int arg)上增加了超時方法,超時時間內獲取返回true,否則false
void acquireShared(int arg) 共享式獲取同步狀態,和獨佔式區別在同一時刻可以有多個執行緒獲取到同步狀態
void acquireSharedInterruptibly(int arg) 和上面的方法相同但是響應中斷
boolean tryAcquireSharedNanos(int arg,long nanos) 在上面方法基礎上增加了超時
boolean release(int arg) 獨佔式的釋放同步狀態,該方法在釋放同步狀態之後,將同步佇列中第一個節點包含的執行緒喚醒
boolean releaseShared(int arg) 共享式的釋放同步狀態
Collection getQueuedThreads 獲取等待在同步佇列上的執行緒集合

用CountDownLatch驗證

確定訪問模式

CountDownLatch支援多執行緒訪問,但是對資源數做限制。所以首先是共享的。就CountDownLatch功能來說,執行緒的先後順序也並不重要,所以不需要公平
在這裡插入圖片描述
可以看到,這裡實現的靜態內部類繼承了AQS並重寫了關於shared(共享的)方法。

確定資源數

CountDownLatch的資源數不確定,由傳入自定義同步元件的引數決定。
在這裡插入圖片描述

組合自定義同步器

這裡特別靈活。像CountDownlatch只是進行了一層簡單的封裝。
在這裡插入圖片描述

用Semaphore驗證

確定訪問模式

Semaphore支援多執行緒訪問,但是對資源數做限制。所以首先是共享的。Semaphore可以是公平的。
在這裡插入圖片描述
可以看到,這裡實現的靜態內部類繼承了AQS並重寫了關於shared(共享的)方法。需要注意這裡reducePermits和drainPermits不是重寫的方法,而是自定義的方法。

在這裡插入圖片描述
這一段程式碼實現了公平機制,其中:
在這裡插入圖片描述
在這裡插入圖片描述
公平和非公平的區別在於這段程式碼:

if (hasQueuedPredecessors())
	return -1;

這是AQS裡的方法,用來判斷當前執行的執行緒是否位於佇列頭部。因為佇列是後進先出的,所以頭結點是最先進去的,也就是公平的定義。

確定資源數

Semaphore的資源數不確定,由傳入自定義同步元件的引數決定。
在這裡插入圖片描述

組合自定義同步器

Semaphore在組合時就複雜了非常多,但是還是圍繞shared體系的方法,所以一開始就要確定是否是共享的。

在這裡插入圖片描述

自定義同步元件Mutex(獨佔鎖)

ReentrantLock這裡不展開講了,首先它是獨佔的,然後也可以是公平的,資源數恆定為1。

獨佔鎖也就是在同一時刻只有一個執行緒獲取到鎖,而其他請求鎖的執行緒只能在同步佇列中排隊~這裡用實現自定義同步元件Mutex為例:

確定訪問模式

獨佔鎖執行緒獨佔資源,所以用非shared那套。可以實現公平也可以不實現公平,這裡偷個懶實現非公平的。

確定資源數

在鎖這裡感覺資源稱為狀態好點ヽ( ̄▽ ̄)و。兩個狀態0,1。0代表獲取1個執行緒獲取到了同步資源,1代表沒有執行緒獲取到資源。

組合自定義同步器

這裡由於獨佔鎖也是鎖,可以作為一種Lock的實現,實現Lock。這一步更多的是思考這個自定義同步器要實現什麼功能。對於大部分的功能類庫中都有實現。

以下是完整程式碼:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class Mutex implements Lock {

    private final Sync sync = new Sync();

    private static class Sync extends AbstractQueuedSynchronizer {
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            } else return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        Condition newCondition(){ return new ConditionObject();}
    }

    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    public boolean isLocked() {
        return sync.isHeldExclusively();
    }

    public boolean hasQueuedThreads() throws InterruptedException{
        return sync.hasQueuedThreads();
    }

    @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() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}

該例項中,獨佔式Mutex是一個自定義同步元件。

參考文獻

《Java併發程式設計的藝術》

相關文章