面試刷題22:CAS和AQS是什么?


image.png




java並發包提供的同步工具和線程池,底層是基於什么原理來設計和實現的呢?這個非常重要。


我是李福春,我在准備面試,今天的題目是:

CAS和AQS是什么?

答:CAS是一系列的操作集合,獲取當前值進行計算,如果當前值沒有改變,表示線程沒有被占用,直接更新成功,否則,進行重試或者返回成功或者失敗。 他是java並發工具包中lock-free的基礎嗎,依賴底層的cpu提供的特定指令實現。底層依賴於Unsafe的本地對象來實現。

AQS: 全稱是AbstractQueuedSynchronizier,抽象隊列同步器;他是各種同步工具鎖的基礎,比如ReentrantLock, CyclicBairier都是基於AQS來實現的。

CAS


以AtomicInteger為例子來分析一下CAS的應用。


首先看內部結構:

image.png



然后分析一下他的一個利用CAS實現的原子操作。 ```java public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!weakCompareAndSetInt(o, offset, v, v + delta)); return v; } ``` 這是一個自旋操作,利用Unsafe比較內存的偏移量,基於cpu指令保證修改的原子可見性。


使用CAS也是有缺點的:
1,自旋次數是假定沖突情況很少的理想情況,但是情況不理想容易過度消耗CPU;
2, ABA問題,一般使用加版本號的 AtomicStampedRefence來搞定;


在實際的工作中如何使用CAS來保證同步操作呢?

可以基於AtomicFieldLongUpdater來實現。比如下面是一個基於CAS實現的同步更新數據庫索引的例子,代碼如下:
 public static class AtomicBTreePartition{

        private volatile long lock;

        private static final AtomicLongFieldUpdater<AtomicBTreePartition>
            lockFieldUpdater
                =AtomicLongFieldUpdater
            .newUpdater(AtomicBTreePartition.class,"lock");

        public void acquireLock(){

            long t = Thread.currentThread().getId();

            while (!lockFieldUpdater.compareAndSet(this, 0, 1)){
                //數據庫操作
            }
        }

        public void releaseLock(){

        }

    }



AQS

她的組成核心主要有3個部分。

file


下面以ReentrantLock的非公平鎖為例,分析一下它的加鎖和釋放鎖的實現。
加鎖操作代碼:

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

繼續跟進:

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}


非公平鎖的獲取鎖操作:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();// 獲取當前AQS內部狀態量
    if (c == 0) {
        // 0表示無人占有,則直接用CAS修改狀態位,
      if (compareAndSetState(0, acquires)) {
          // 不檢查排隊情況,直接爭搶
          setExclusiveOwnerThread(current);  
         //並設置當前線程獨占鎖
          return true;
      }
    } else if (current == getExclusiveOwnerThread()) { 
        //即使狀態不是0,也可能當前線程是鎖持有者,因為這是再入鎖
      int nextc = c + acquires;
      if (nextc < 0) // overflow
          throw new Error("Maximum lock count exceeded");
      setState(nextc);
      return true;
  }
  return false;
}

排隊競爭:

final boolean acquireQueued(final Node node, int arg) {
      boolean interrupted = false;
      try {
      for (;;) {// 循環
          final Node p = node.predecessor();// 獲取前一個節點
          if (p == head && tryAcquire(arg)) { 
              // 如果前一個節點是頭結點,表示當前節點合適去tryAcquire
              setHead(node); // acquire成功,則設置新的頭節點
              p.next = null; // 將前面節點對當前節點的引用清空
              return interrupted;
          }
          if (shouldParkAfterFailedAcquire(p, node)) 
              // 檢查是否失敗后需要park
              interrupted |= parkAndCheckInterrupt();
      }
       } catch (Throwable t) {
      cancelAcquire(node);// 出現異常,取消
      if (interrupted)
              selfInterrupt();
      throw t;
      }

小結


本節介紹了CAS和AQS的概念,然后以AtomicInteger為例切入原子操作是怎么利用CAS來保證的。
最后,以ReentrantLock的加鎖操作,跟進了是如何利用AQS來保證內部的不同操作的。


算是初步探究了同步的實現原理。





image.png

原創不易,轉載請注明出處。


免責聲明!

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



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