十、JUC之AQS

AQS即AbstractQueuedSynchronizer,是jdk提供的一個用於實現阻塞鎖和依賴於先進先出等待隊列的相關同步器(信號量,事件等)。AQS是concurrent包的基石。

AQS 狀態。

它依賴一個int類型的原子變量來表示狀態。

private volatile int state;//共享變量,使用volatile修飾保證線程可見性

對state操作的基本方法

protected final int getState()

protected final void setState(int newState)

protected final boolean compareAndSetState(int expect, int update)

子類必須實現其protected的方法用於改變這個狀態和根據該狀態的獲取和釋放定義狀態的含義。

AQS實現同步器

AQS支持兩種獨佔共享兩種同步方式。獨佔的實現有ReentrantLock,共享的實現有CountDownLatch,Semaphore。讀寫鎖的讀鎖用了共享鎖,寫鎖用來獨佔鎖。AQS是通過重寫以下方法來實現同步。

  1. 共享鎖是滿足一定條件的都可以獲取鎖,具體實現邏輯在tryAcquireShared中實現。
  2. 獨佔鎖是同時只能有一個線程佔用鎖。

重寫方法

AQS用了模板設計模式,我們不用關心獲取資源失敗,線程排隊,線程阻塞/喚醒等一系列複雜的實現,這些都在AQS中爲我們處理好了。我們只需要負責好自己的那個環節就好,也就是獲取/釋放共享資源state。用戶可以根據自己的要求很輕鬆的擴展AQS。

實現獨佔鎖要重寫的方法

//獨佔式獲取同步狀態,試着獲取,成功返回true,反之爲false
    protected boolean tryRelease(int arg)
    //釋放鎖方法,返回true代表成功釋放
    protected boolean tryAcquire(int arg)
    
    是否在獨佔模式下被線程佔用,true爲被佔用。
    protected boolean isHeldExclusively()

實現共享鎖要重寫的方法

//返回值大於0則是成功佔用鎖,小於0則是阻塞
    protected int tryAcquireShared(int arg)
    //釋放鎖方法,返回true代表成功釋放
    protected boolean tryReleaseShared(int arg)

使用

獨佔和共享方式都有3個佔用鎖方法和一個釋放鎖方法。內部調用了上面實現的方法。

獨佔鎖使用方法

public final void acquire(int arg)

//通過首先檢查中斷狀態,如果中斷,中止
public final void acquireInterruptibly(int arg)
                                throws InterruptedException
//阻塞時長超出設置事件則跳出                                
public final boolean tryAcquireNanos(int arg,
                                     long nanosTimeout)
                              throws InterruptedException
                              
public final boolean release(int arg)

共享鎖使用方法

//佔用共享鎖方法,最終都是調用了實現的tryAcquireShared方法。
public final void acquireShared(int arg)

//通過首先檢查中斷狀態,如果中斷,中止
public final void acquireSharedInterruptibly(int arg)
                                      throws InterruptedException
//阻塞時長超出設置事件則跳出。                              
public final boolean tryAcquireSharedNanos(int arg,
                                           long nanosTimeout)
                                    throws InterruptedException

//釋放鎖方法
public final boolean releaseShared(int arg)

獨佔鎖實現

JDK文檔中帶的一個獨佔鎖例子

import java.util.concurrent.locks.AbstractQueuedSynchronizer;


public class Mutex implements java.io.Serializable {
    //實現的獨佔鎖
    private static class Sync extends AbstractQueuedSynchronizer {
        //是否處於佔用狀態
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
        //這裏獲取鎖,通過cas將state設置爲1,並設置獨佔線程。
        public boolean tryAcquire(int acquires) {
            if (compareAndSetState(0, 1)) {
                //設置當前擁有獨佔訪問權限的線程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        //釋放鎖,將同步狀態置爲0
        protected boolean tryRelease(int releases) {
            if (getState() == 0) throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }
        //同步對象完成一系列複雜的操作,我們僅需指向它即可
        private final Sync sync = new Sync();
        //加鎖操作,代理到acquire(模板方法)上就行,acquire會調用我們重寫的tryAcquire方法
        public void lock() {
            sync.acquire(1);
        }
        public boolean tryLock() {
            return sync.tryAcquire(1);
        }
        //釋放鎖,代理到release(模板方法)上就行,release會調用我們重寫的tryRelease方法。
        public void unlock() {
            sync.release(1);
        }
        public boolean isLocked() {
            return sync.isHeldExclusively();
        }
}

共享鎖實現 CountDownLatch

CountDownLatch就是通過AQS實現的共享鎖,實現簡單,容易理解。

基於AQS實現的共享鎖

CountDownLatch內部類

private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        
        //CountDownLatch構造函數中會將count傳過來,構建Sync鎖。
        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }
        
        //count==0的時候才能成功佔用鎖。
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
        
        //countDown方法會調用這裏的邏輯,每次count-1,直到0才成功釋放。
        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

CountDownLatch.await()

/**
     這裏調用了sync的acquireSharedInterruptibly(1),裏面最終會調用重寫的tryAcquireShared方法。可以看到只有state==0的時候才能佔用成功,不然會阻塞。這裏傳入的參數1並沒有作用。
    */
   public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

CountDownLatch.countDown()

/**
 這裏調用了AQS的releaseShared,會使用重寫的tryReleaseShared實現。每次都會使state-1,直到state==0成功釋放鎖。
*/
public void countDown() {
        sync.releaseShared(1);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章