什么是AQS
AQS是AbustactQueuedSynchronizer的简称,它是一个Java提高的底层同步工具类,用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。AQS的主要作用是为Java中的并发同步组件提供统一的底层支持,例如ReentrantLock,CountdowLatch就是基于AQS实现的,用法是通过继承AQS实现其模版方法,然后将子类作为同步组件的内部类。
同步队列
同步队列是AQS很重要的组成部分,它是一个双端队列,遵循FIFO原则,主要作用是用来存放在锁上阻塞的线程,当一个线程尝试获取锁时,如果已经被占用,那么当前线程就会被构造成一个Node节点假如到同步队列的尾部,队列的头节点是成功获取锁的节点,当头节点线程释放锁时,会唤醒后面的节点并释放当前头节点的引用。
独占锁的获取和释放流程
获取
- 调用入口方法acquire(arg)
- 调用模版方法tryAcquire(arg)尝试获取锁,若成功则返回,若失败则走下一步
- 将当前线程构造成一个Node节点,并利用CAS将其加入到同步队列到尾部,然后该节点对应到线程进入自旋状态
- 自旋时,首先判断其前驱节点释放为头节点&&是否成功获取同步状态,两个条件都成立,则将当前线程的节点设置为头节点,如果不是,则利用LockSupport.park(this)将当前线程挂起 ,等待被前驱节点唤醒
释放
- 调用入口方法release(arg)
- 调用模版方法tryRelease(arg)释放同步状态
- 获取当前节点的下一个节点
- 利用LockSupport.unpark(currentNode.next.thread)唤醒后继节点(接获取的第四步)
共享锁的获取和释放流程
获取共享锁:
- 调用acquireShared(arg)入口方法
- 进入tryAcquireShared(arg)模版方法获取同步状态,如果返返回值>=0,则说明同步状态(state)有剩余,获取锁成功直接返回
- 如果tryAcquireShared(arg)返回值<0,说明获取同步状态失败,向队列尾部添加一个共享类型的Node节点,随即该节点进入自旋状态
- 自旋时,首先检查前驱节点释放为头节点&tryAcquireShared()是否>=0(即成功获取同步状态)
- 如果是,则说明当前节点可执行,同时把当前节点设置为头节点,并且唤醒所有后继节点
- 如果否,则利用LockSupport.unpark(this)挂起当前线程,等待被前驱节点唤醒
释放锁:
- 调用releaseShared(arg)模版方法释放同步状态
- 如果释放成,则遍历整个队列,利用LockSupport.unpark(nextNode.thread)唤醒所有后继节点
独占锁和共享锁在实现上的区别
- 独占锁的同步状态值为1,即同一时刻只能有一个线程成功获取同步状态
- 共享锁的同步状态>1,取值由上层同步组件确定
- 独占锁队列中头节点运行完成后释放它的直接后继节点
- 共享锁队列中头节点运行完成后释放它后面的所有节点
- 共享锁中会出现多个线程(即同步队列中的节点)同时成功获取同步状态的情况
AQS独占和共享锁,ReentantLock为独占锁。
ReentantReadWriteLock中readLock()为共享锁,writeLock()为独占锁:
读锁与读锁可以共享
读锁与写锁不可以共享
写锁与写锁不可以共享