JAVA併發編程-AQS原理&RenntrantLock源碼導讀

 1.文章目錄

  • AQS概述,類結構,源碼導讀;
  • RenntractLock源碼,實現細節導讀;

2.AQS概述&類結構

  AbstractQueuedSynchronizer 抽象同步隊列簡稱 AQS ,它是實現同步器的基礎組件, 併發包中鎖的底層就是使用 AQS 實現的;

  • AQS 是一個FIFO 的雙向隊列,其內部通過節點 head tail 首和隊尾元素,隊列元素的類型爲Node 其中 Node 中的 thread 變量用來存放進入 AQS 隊列裏面的線程:
  • Node 節點內部的 SHARED 用來標記該線程是獲取共享資源資源時被阻 掛起後放入AQS 隊列的, EXCLUSIVE 用來標記線程是獲取獨佔資源時被掛起後放入 AQS 隊列的,waitStatu 記錄當前線程等待狀態,可以爲 CANCELLED (線程被取消了)、 SIGNAL 線程需要被喚醒)、 ONDITION (線程在條件隊列裏面等待〉、 PROPAGATE 釋放共享資源時需要通知其他節點; prev 記錄當前節點的前驅節點, next 記錄當前節點的 後繼節點
  • AQS 維持了的狀態信息 state,可以通過 getState setState compareAndSetState 函數修改其值,對於 ReentrantLock來說, state 可以用來表示前線程獲取鎖的可重入次數 ;對於 讀寫鎖 ReentrantReadWri teLock 來說 state16 位表示讀狀態,也就是獲取該讀鎖的次數,低16位表示獲取到寫鎖的線程的可重入次數; 對於 semaphore 來說 state 用來表示當前可用信號的 數:對於 CountDownlatch 來說,state C表示 數器當前的值;
  • AQS 有個內部類ConditionObject 用來結合鎖實現線程同步 ConditionObject 可以 直接訪問 AQS 內部的變量,比如 state 態值和 AQS隊列。ConditionObject 是條 變量 個條件變量對應 件隊列 (單向鏈表隊列),其用來存放調用條件變 await 方法後被阻塞的線程,如類圖所示 這個條件隊列的頭、尾元素分別爲firstWaiter,lastWaiter;

源碼導讀

基本屬性:看上面的圖可以直到基本屬性:使用Unsafe機制,記錄變量偏移量,方便CAS操作


    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;

    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
     }

acquire(int arg):獲取獨佔資源方法

 public final void acquire(int arg) {
        // 首先調用tryAcquire嘗試獲取資源
        if (!tryAcquire(arg) &&
            ,失敗則(Node.EXCLUSIVE)狀態進去等待隊列,並且LockSupport.park掛起自己
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

addWaiter方法

 private Node addWaiter(Node mode) {
        // 創建節點,記錄當前線程
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            CAS插入
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 如果上面沒有插入成功,則循環插入
        enq(node);
        return node;
    }
  private Node enq(final Node node) {
        // 循環插入
        for (;;) {
            Node t = tail;
            // 尾部爲NULL,則head肯定也是NULl,創建比賦值
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                // 設置tail節點
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

realease(int arg):釋放資源

  public final boolean release(int arg) {
        // 調用tryRelease方法,主要是修改state的值
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                // 釋放AQS隊列一個被阻塞的線程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

acquireShared(int arg)獲取共享資源

// 首先嚐試獲取資源,如果不成功,LockSupport.park當前線程,封裝Node.ShARED進入AQS隊列
public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

doRelaseShared:釋放共享資源

     // 嘗試釋放資源(設置state),如果成功則激活一個線程,判斷是否滿足自己的需要,不滿足再次進去隊列
     public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
  • 到此基本的核心方法已經導讀結束,AQS作爲隊列同步的抽象,對基本方法做了實現,tryAcquire,treRelease等方法要交給子類來實現,比如可重入鎖,不可重入鎖的設計肯定不同的;後續對ReentrantLock源碼導讀會更加清晰;
  
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

   
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

    
    protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }

條件變量Condition

     這個變量類似於wait,notify的作用,線程同步用的,比較基礎;可以自己探索下;

3.ReentrantLock類結構&源碼導讀

  • 可以看出,ReentrantLock實現了Lock接口,實現了基本接口,藉助Sync(繼承AQS)實現真正的線程同步策略;
  • ReentrantLock 是可重入的獨佔鎖只能有一個線程可獲取該鎖,其他獲取該鎖的線程會被阻塞而被放入該鎖的AQS阻塞隊列裏面;
  • 我們平常用這個的鎖,有公平鎖,非公平鎖(默認)兩種形式;因此內部封裝了兩種實現形式;

初始化方法:

    public ReentrantLock() {
        sync = new NonfairSync();
    }

    
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
  • 上述代碼提供了兩種初始化方式,也是我們最常用的;默認爲非公平策略,也可以根據fair參數指定;

其他核心方法:

   public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

   public void unlock() {
        sync.release(1);
    }

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

    public int getHoldCount() {
        return sync.getHoldCount();
    }

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

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

   // sync是否是FairSync的子類/類
    public final boolean isFair() {
        return sync instanceof FairSync;
    }

   
    protected Thread getOwner() {
        return sync.getOwner();
    }

  
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

 
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

    public final int getQueueLength() {
        return sync.getQueueLength();
    }

    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
  • 可以看出都是藉助Sync(AQS)變量實現的;模板方法設計;
  • 因此我們看Sync的實現

Sync類實現

  •  該類繼承AQS,對AQS的方法進行了重寫,根據Reentrant策略進行設計;
  • 具體的lock,tryRe
 abstract static class Sync extends AbstractQueuedSynchronizer {
.........
   // 交給fairSync,NoFairSync實現
   abstract void lock();
}

final boolean nonfairTryAcquire(int acquires)

// 非公平策略嘗試獲取資源
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 如果c=0則表示沒有鎖佔有
            if (c == 0) {
                // CAS設置state=acquires
                if (compareAndSetState(0, acquires)) {
                     // 設置鎖擁有線程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 也是可重入鎖的設計特點
            // 鎖被佔有了,看下是不是自己
            else if (current == getExclusiveOwnerThread()) {
                // 是自己,對c進行加
                int nextc = c + acquires;
                // 超過最大數
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

tryRelease:釋放資源

protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            // 如果不是鎖擁有線程,拋出異常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
           // 如果c=0了,釋放資源完了
            if (c == 0) {
                // free=true,鎖擁有線程爲Null
                free = true;
                setExclusiveOwnerThread(null);
            }
             // CAS設置鎖狀態
            setState(c);
            return free;
        }

isHeldExclusively

//判斷鎖是否被自己佔有,判斷食不知自己就好了
 protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
}

幾個簡單方法:

     // 生產條件變量  
    final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // 獲取鎖Owner

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
        // 獲取鎖持有數
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
        // 是否被佔有
        final boolean isLocked() {
            return getState() != 0;
        }
  • 到這幾個基本方法介紹完了,我們看如何實現公平策略,非公平策略

FairSync&NoFairSync源碼

NoFairSync類源碼

  • 繼承Sync對lock,tryAcquire做了重寫;
  • 不公平體現在哪:第一個調用lock方法的線程就會獲得,隨機的
static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        // CAS設置state狀態
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
               // 失敗,調用AQS的acquire,前面已經介紹,如果嘗試獲取失敗後,進入阻塞隊列
                acquire(1);
        }
         // 服用父類Sync的方法
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

FairSync類源碼

  • 繼承Sync對lock,tryAcquire做了重寫;
  • 公平體現在:根據阻塞隊列從前向後以此獲得鎖,根據調用lock的順序進行獲得鎖;
  • hasQueuedPredecessors實現了公平策略;
static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
         // 調用AQS上層方法,還是會掉tryAcquire方法
        final void lock() {
            acquire(1);
        }

        // 公平策略的實現
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 如果鎖釋放了
            if (c == 0) {
                
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //加state
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

    // 如果當前線程節點有前驅節點則返回住時, 如果當前AQS隊列空或者當前線程節點是 AQS 第一個節 
   點則返回 false 。其中如果 h==t 說明當前隊列,直接返回 false ;
   public final boolean hasQueuedPredecessors() {
        Node t = tail; 
        Node h = head;
        Node s;

        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

4.總結

  • 以上是AQS,ReentrantLock的基本實現原理,對於更多細節可以自己閱讀;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章