Java並發包源碼學習之AQS框架(一)概述


AQS其實就是java.util.concurrent.locks.AbstractQueuedSynchronizer這個類。 閱讀Java的並發包源碼你會發現這個類是整個java.util.concurrent的核心之一,也可以說是閱讀整個並發包源碼的一個突破口。

比如讀ReentrantLock的源碼你會發現其核心是它的一個內部類Sync:

aqs-overview.png

 

整個包中很多類的結構都是如此,比如Semaphore,CountDownLatch都有一個內部類Sync,而所有的Sync都是繼承自AbstractQueuedSynchronizer。 所以說想要讀懂Java並發包的代碼,首先得讀懂這個類。

AQS簡核心是通過一個共享變量來同步狀態,變量的狀態由子類去維護,而AQS框架做的是:

  • 線程阻塞隊列的維護
  • 線程阻塞和喚醒

共享變量的修改都是通過Unsafe類提供的CAS操作完成的。AbstractQueuedSynchronizer類的主要方法是acquirerelease,典型的模板方法, 下面這4個方法由子類去實現:

protected boolean tryAcquire(int arg)
protected boolean tryRelease(int arg)
protected int tryAcquireShared(int arg)
protected boolean tryReleaseShared(int arg)

acquire方法用來獲取鎖,返回true說明線程獲取成功繼續執行,一旦返回false則線程加入到等待隊列中,等待被喚醒,release方法用來釋放鎖。 一般來說實現的時候這兩個方法被封裝為lockunlock方法。

下面的SimpleLock類實現了一個最簡單非重入的互斥鎖的功能,實際上它就是ThreadPoolExecutor$Worker的實現(以后的文章會提到這個類)。

class SimpleLock extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -7316320116933187634L;

    public SimpleLock() {

    }

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

    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

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

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

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

    public boolean isLocked() {
        return isHeldExclusively();
    }
}
public static void main(String[] args) throws InterruptedException {
    final SimpleLock lock = new SimpleLock();
    lock.lock();

    for (int i = 0; i < 10; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println(Thread.currentThread().getId() + " acquired the lock!");
                lock.unlock();
            }
        }).start();
        // 簡單的讓線程按照for循環的順序阻塞在lock上
        Thread.sleep(100);
    }

    System.out.println("main thread unlock!");
    lock.unlock();
}

運行上面的測試代碼,結果如下:

main thread unlock! 9 acquired the lock! 10 acquired the lock! 11 acquired the lock! 12 acquired the lock! 13 acquired the lock! 14 acquired the lock! 15 acquired the lock! 16 acquired the lock! 17 acquired the lock! 18 acquired the lock!

會發現等待的線程是按照阻塞時的順序依次獲取到鎖的。 這是因為AQS是基於一個叫CLH lock queue的一個變種來實現線程阻塞隊列的,我們下一篇文章就來簡單了解下CLH lock queue。

后續文章計划如下:

  • 《Java並發包源碼學習之AQS框架(二)CLH lock queue和自旋鎖》
  • 《Java並發包源碼學習之AQS框架(三)LockSupport》
  • 《Java並發包源碼學習之AQS框架(四)AbstractQueuedSynchronizer源碼分析》
  • 《Java並發包源碼學習之AQS框架(五)ConditionObject源碼分析》

  ……

  • 《Java並發包源碼學習之鎖(一)概述》
  • 《Java並發包源碼學習之鎖(二)ReentrantLock源碼分析》

  ……

  • 《Java並發包源碼學習之線程池(一)概述》
  • 《Java並發包源碼學習之線程池(二)ThreadPoolExecutor源碼分析》

  ……

學習Java並發包源碼的初衷是為了搞清之前遇到的一個問題,其實很早之前就打算看這塊的源碼但一直沒看下去,所以說 看源碼一定要有目的不能為了看而看。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM