Java AQS底層原理解析

AQS底層原理

AQS(AbstractQueuedSynchronizer)是一個抽象同步隊列,JUC(java.util.concurrent)中不少同步鎖都是基於AQS實現的。java

AQS的基本原理就是當一個線程請求共享資源的時候會判斷是否可以成功操做這個共享資源,若是能夠就會把這個共享資源設置爲鎖定狀態,若是當前共享資源已經被鎖定了,那就把這個請求的線程阻塞住,也就是放到隊列中等待。編程


state變量:

  • AQS中有一個被volatile聲明的變量用來表示同步狀態
  • 提供了getState()setState()compareAndSetState()方法來修改state狀態的值併發

    // 返回同步狀態的當前值
    protected final int getState() {  
      return state;
    }
    
    // 設置同步狀態的值
    protected final void setState(int newState) { 
      state = newState;
    }
    
    // CAS操做修改state的值
    protected final boolean compareAndSetState(int expect, int update) {
      return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

對共享資源的操做方式:

上面說了AQSJUC中不少同步鎖的底層實現,鎖也分不少種,有像ReentrantLock這樣的獨佔鎖,也有ReentrantReadWriteLock這樣的共享鎖,因此AQS中也必然是包含這兩種操做方式的邏輯ide

  1. 獨佔式ui

    • 獲取資源的時候會調用acquire()方法,這裏面會調用tryAcquire()方法去設置state變量,若是失敗的話就把當前線程放入一個Node中存入隊列
    public final void acquire(int arg) {
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
     }
    • 釋放資源的時候是調用realase()方法,會調用tryRelease()方法修改state變量,調用成果後會去喚醒隊列中Node裏的線程,unparkSuccessor()方法就是判斷當前state變量是否符合喚醒的標準,若是合適就喚醒,不然繼續放回隊列
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    • 注意tryAcquire()tryRelease()方法在AQS中都是空的,前面說了JUC中不少同步鎖都是基於AQS實現,因此加鎖和釋放鎖的邏輯都還不肯定,所以是要在這些同步鎖中實現這兩個方法
    protected boolean tryAcquire(int arg) {
         throw new UnsupportedOperationException();
    }
    
    protected boolean tryRelease(int arg) {
         throw new UnsupportedOperationException();
    }
  2. 共享式this

    • 獲取資源會調用acquireShared()方法,會調用tryAcquireShared()操做state變量,若是成功就獲取資源,失敗則放入隊列線程

      public final void acquireShared(int arg) {
          if (tryAcquireShared(arg) < 0)
              doAcquireShared(arg);
      }
    • 釋放資源是調用releaseShared()方法,會調用tryReleaseShared()設置state變量,若是成功就喚醒隊列中的一個Node裏的線程,不知足喚醒條件則還放回隊列中code

      public final boolean releaseShared(int arg) {
         if (tryReleaseShared(arg)) {
             doReleaseShared();
             return true;
         }
         return false;
      }
    • 和獨佔式同樣,tryAcquireShared()tryReleaseShared()也是須要子類來提供對象

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

條件變量Condition

Condition中的signal()await()方法相似與notify()wait()方法,須要和AQS鎖配合使用。隊列

public static void main(String[] args) throws InterruptedException {

    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            lock.lock();
            System.out.println(" t1 加鎖");
            System.out.println("t1 start await");
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1 end await");
            lock.unlock();
        }
    });
    thread1.start();

    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            lock.lock();
            System.out.println(" t2 加鎖");
            System.out.println("t2 start signal");
            condition.signal();
            System.out.println("t2 end signal");
            lock.unlock();
        }
    });
    thread2.start();

}
  • AQS中的原理

    上面lock.newCondition()實際上是new一個AQSConditionObject內部類的對象出來,這個對象裏面有一個隊列,當調用await()方法的時候會存入一個Node節點到這個隊列中,而且調用park()方法阻塞當前線程,釋放當前線程的鎖。而調用singal()方法則會移除內部類中的隊列頭部的Node,而後放入AQS中的隊列中等待執行機會

  • 一樣的,AQS並無實現newCondition()方法,也是須要子類本身去實現

總結:

本文內容大部分都是閱讀了<<Java併發編程之美>>這本書的內容,主要講了整個AQS的大體原理,和幾個最重要的方法,其實整個AQS的源碼是很複雜的,可是瞭解大體原理已經對咱們熟悉運用那些JUC中的同步鎖有很大的幫助。

相關文章
相關標籤/搜索