1. 程式人生 > >JUC併發程式設計基石AQS原始碼之結構篇

JUC併發程式設計基石AQS原始碼之結構篇

前言

AQS(AbstractQueuedSynchronizer)算是JUC包中最重要的一個類了,如果你想了解JUC提供的併發程式設計工具類的程式碼邏輯,這個類絕對是你繞不過的。我相信如果你是第一次看AQS原始碼肯定是一臉懵逼,一個個方法跳來跳去一會就繞蒙了。所以把整個程式碼骨架搞明白是你看懂AQS原始碼的第一步。本篇文章只說程式碼結構,之後的篇章會講解AQS具體的執行邏輯。

頂級介面Lock

public interface Lock {

    void lock();
  
    void unlock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    Condition newCondition();
}

我們一直強調面向介面程式設計的思想,看原始碼也是先從介面入手。Lock算是JUC提供的鎖中的頂級介面,我們平常使用的ReentrantLock、ReadWriteLock等直接或間接的都實現了這個介面。這個介面主要是用來規定我們怎樣去加鎖和解鎖,將加鎖和解鎖的方法暴露出去。下面的虛擬碼是一個典型的加鎖解鎖方式,多麼簡單,這就是面向介面的藝術。

 Lock l = ...;
 l.lock();
 try {
   // 自己的邏輯程式碼
 } finally {
   l.unlock();
 }

程式碼結構

到現在我們知道了怎麼加鎖和解鎖的方式,下一步自己通過介面去實現一個加鎖類。這個比你直接看原始碼更重要。Doug Lea(AQS原始碼作者)大神在AQS類註解上就給我們提供了一個示例類Mutex。看原始碼類註解和方法註解也是你理解原始碼的一個重要渠道,還可以鍛鍊自己的英文。

實現Lock介面

首先要實現Lock介面,將加鎖和解鎖方法暴露出去。我們以加鎖為例

class Mutex implements Lock, java.io.Serializable {

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

程式碼很簡單,呼叫了sync.acquire(1)方法,所以這個方法是加鎖邏輯的入口,往下看

內部類繼承AQS

現在將加鎖方法暴露出去了,具體的加鎖邏輯則需要AQS類了。AQS是一個抽象類,我們要使用它則需要繼承,實現抽象方法。AQS加鎖採用的是模板的設計模式,加鎖以及鎖失敗的後續處理的整體流程程式碼已經實現,我們只需要實現我們需要的具體加鎖方式即可。

class Mutex implements Lock, java.io.Serializable {

   // The sync object does all the hard work. We just forward to it.
    private final Sync sync = new Sync();
  
    // 內部類繼承AQS
    private static class Sync extends AbstractQueuedSynchronizer {

        // 實現抽象方法
        public boolean tryAcquire(int acquires) {
            assert acquires == 1; // Otherwise unused
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }  
    }
}

我們會發現Sync繼承了AQS,但是沒有sync.acquire()這個方法,那麼這個方法肯定來源於父類了。

AQS的acquire方法

public abstract class AbstractQueuedSynchronizer{
  
  //加鎖的入口方法,模板的設計模式
  public final void acquire(int arg) {
      if (!tryAcquire(arg) &&
          acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
          selfInterrupt();
  }
	
  //具體的加鎖邏輯,需要自己實現
	protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

acquire方法定義了整個的加鎖流程,而且使用了設計模式中的模板模式。

整體呼叫流程

從上面的流程可以看出,就這四個步驟就涉及到了三個類

  • Mutex.lock為暴露出去的加鎖方法

  • AQS.acquire是加鎖的模板方法,實現了加鎖邏輯的整個流程

  • Sync.tryAcquire,圖中的綠色部分,是一個抽象方法需要自己實現,針對不同的鎖型別如公平鎖、非公平鎖、共享鎖、獨佔鎖等有不同的實現方式。

  • AQS.acquireQueued是加鎖失敗後的邏輯,將執行緒入隊,這個後面講AQS原始碼會重點講。

    解鎖操作的流程和加鎖類似,讀者可以自己看一下解鎖的流程。

自定義加鎖類的原始碼

下面的程式碼是Mutex類的整體程式碼,有需要的可以在自己的IDE中感受一下整體的結構。

class Mutex implements Lock, java.io.Serializable {

    public static void main(String[] args) {
        Lock lock = new Mutex();
        lock.lock();
        try {

        }finally {
            lock.unlock();
        }
    }

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

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

    // Our internal helper class
    private static class Sync extends AbstractQueuedSynchronizer {
        // Reports whether in locked state
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        // Acquires the lock if state is zero
        public boolean tryAcquire(int acquires) {
            assert acquires == 1; // Otherwise unused
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        // Releases the lock by setting state to zero
        protected boolean tryRelease(int releases) {
            assert releases == 1; // Otherwise unused
            if (getState() == 0) throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        // Provides a Condition
        Condition newCondition() {
            return new ConditionObject();
        }

        // Deserializes properly
        private void readObject(ObjectInputStream s)
                throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    // The sync object does all the hard work. We just forward to it.
    private final Sync sync = new Sync();

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

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

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

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

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

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

下一篇我們將根據上面所說的來分析ReentrantLock類的程式碼結構

如有不實,還望指