AQS实现原理

98 阅读3分钟

了解了ReentrantLock和Semaphore实现原理,不难看出AQS担当着核心左右,AQS为独占锁和共享锁提供了基础框架能力,通过对AQS的封装,我们可以轻松实现线程状态管理。

AQS对象组成

对于AQS而言,其核心为state和先进先出的等待队列,其中state使用volatile修饰,保证其可见性和有序性,先进先出的等待队列使用双向链表实现,对于一个AQS对象而言,其组成如下图所示:

AQS.drawio

AQS同步等待队列

AQS中同步等待队列通过Node对象链表实现,一个完整的等待队列如下图所示:

node_queue.drawio

上述中enq函数代码如下:

 private Node enq(final Node node) {
     for (;;) {
         Node t = tail;
         if (t == null) { 
             // 初始化head和tail,刚开始head,tail都指向这里的new Node
             if (compareAndSetHead(new Node()))
                 tail = head;
         } else {
             node.prev = t;
             if (compareAndSetTail(t, node)) {
                 t.next = node;
                 return t;
             }
         }
     }
 }

完整的AQS数据结构(同步队列+条件队列)

ReentrantLock_DataDiagrams.drawio

实现AQS(根据业务逻辑自定义锁)

为了便于开发者给予AQS实现一个自定义锁,AQS主要提供了5个函数供大家覆写,对于没被子类覆写的函数,在其调用时会抛出UnsupportedOperationException异常,如下所示:

函数名称函数说明默认实现适用锁类型备注
tryAcquire尝试获取锁,主要进行锁状态标志state的操作,操作成功后更新state和当前持锁线程抛出UnsupportedOperationException异常独占锁/
tryRelease尝试释放锁,更新锁状态标志state并设置当前持锁线程为null抛出UnsupportedOperationException异常独占锁/
tryAcquireShared共享模式下尝试获取锁抛出UnsupportedOperationException异常共享锁/
tryReleaseShared共享模式下尝试释放锁抛出UnsupportedOperationException异常共享锁/
isHeldExclusively判断锁是否被占用抛出UnsupportedOperationException异常独占锁/共享锁/

下面我们来实现一个不可重入的独占锁(非公平),代码如下:

 import java.io.Serializable;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.AbstractQueuedSynchronizer;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 ​
 public class Mutex implements Lock, Serializable {
     private static class Sync extends AbstractQueuedSynchronizer{
         @Override
         protected boolean tryAcquire(int arg) {
             if (compareAndSetState(0,1)) {
                 setExclusiveOwnerThread(Thread.currentThread());
                 return true;
             }
             return false;
         }
 ​
         @Override
         protected boolean tryRelease(int arg) {
             if (getState() == 0) {
                 throw new IllegalMonitorStateException();
             }
             setExclusiveOwnerThread(null);
             setState(0);
             return true;
         }
 ​
         Condition newCondition() { return new ConditionObject(); }
     }
 ​
     private Sync sync = new Sync();
     @Override
     public void lock() {
         sync.acquire(1);
     }
 ​
     @Override
     public void lockInterruptibly() throws InterruptedException {
         sync.acquireInterruptibly(1);
     }
 ​
     @Override
     public boolean tryLock() {
         return sync.tryAcquire(1);
     }
 ​
     @Override
     public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
         return sync.tryAcquireNanos(1,unit.toNanos(time));
     }
 ​
     @Override
     public void unlock() {
         sync.release(1);
     }
 ​
     @Override
     public Condition newCondition() {
         return sync.newCondition();
     }
 }

编写测试代码如下:

 public static void main(String[] args) {
     Mutex mutex = new Mutex();
     ExecutorService testSynchronizedBlock = Executors.newCachedThreadPool();
     testSynchronizedBlock.execute(new Runnable() {
         @Override
         public void run() {
             // 首次获取锁运行2s后再次获取锁,模拟线程重入操作
             System.out.println(Thread.currentThread().getName()+" enter lock first");
             mutex.lock();
             System.out.println(Thread.currentThread().getName()+" enter lock");
             try {
                 Thread.sleep(2000);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
             System.out.println(Thread.currentThread().getName()+" enter lock again");
             mutex.lock();
             try {
                 Thread.sleep(2000);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
             System.out.println(Thread.currentThread().getName()+" exit lock again");
             mutex.unlock();
             System.out.println(Thread.currentThread().getName()+" exit lock");
             mutex.unlock();
         }
     });
     testSynchronizedBlock.execute(new Runnable() {
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName()+" enter lock before");
             mutex.lock();
             System.out.println(Thread.currentThread().getName()+" enter lock");
             try {
                 Thread.sleep(3000);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
             System.out.println(Thread.currentThread().getName()+" exit lock");
             mutex.unlock();
         }
     });
 }

执行结果如下:

1-4-14-1

可以看到线程pool-1-thread-1和pool-1-thread-2均卡在mutex 锁处,且形成死锁,长期无返回,修改Mutex为ReentrantLock,可以看到代码正常运行,结果如下:

1-4-14-2

这也进一步验证了ReentrantLock可重入的特性。

AQS的常见实现

从前文可知,ReentrantLock和Semaphore都是基于AQS实现,那系统中还有哪些类依赖AQS实现呢?见下表:

类名独占锁/共享锁公平锁/非公平锁可重入备注
ReentrantLock独占锁都支持/
ReentrantReadWriteLock读锁是共享锁/写锁是独占锁(读写锁)都支持/
Semaphore共享锁都支持/
CountDownLatch共享锁公平锁/

AQS,synchronized与volatile

AQS取其代表实现ReentrantLock,三者比较如下表所示:

名称底层实现独占锁/共享锁公平锁/非公平锁可重入重量级锁/轻量级锁多线程特性是否是锁
ReentrantLockjava层锁都支持都支持重量级锁原子性,有序性,可见性
synchronizedmonitorenter/monitoreexit指令(Lock)独占锁非公平锁锁升级,先轻量级锁,再重量级锁原子性,可见性,有序性
volatile内存屏障指令////可见性,有序性