- 介绍:
- 提供了一个基于FIFO队列,可以用于构建锁或者其他同步装置的基础框架,
- 利用了一个int类型表示状态
- 使用方法是继承
- 子类通过继承并通过实现它的方法管理其状态{acquire 和release}的方法操纵状态
- 可以同时实现排它锁和共享锁模式
- 源码内使用:
- 使用类型:
- 独占类型
- 共享类型
- 使用类型:
- 内部属性:
- 队列保存线程引用和线程状态的容器, 将线程对同步器的一次访问,看作一个Node
- Node(节点):
-
属性名称说明
waitStatus 1.SINGAL=-1 : 当前节点的后继节点需要运行,也就是unpark
2.CANCELLED=1:当前节点被取消
3.CONDIITION=-2 : 表示当前节点在等待Condition ,也就是Condition队列
4.propagate=-3:便是当前场景下后序的acquireshare能够得以执行
5. 0 :当前节点在sync队列中,等待获得锁nextWaiter 存储在Condition队列中的后继节点
- Node(节点):
- 队列保存线程引用和线程状态的容器, 将线程对同步器的一次访问,看作一个Node
- 同步队列,依托node构建 :
- 同步器拥有syn队列的3个成员变量:
- 头节点:head 尾节点:tail 状态 :state
- 构造函数:
-
protected AbstractQueuedSynchronizer() { }
-
- 主要接口
- acquire(intr arg)
- 该方法以排他的方式获取锁,对中断不敏感,完成synchronized语义。
- 源码:
- tryacquire需要子类自己去实现,交给子类去实现{以ReentrantLock--fairLock为例子
- 未获取到,就生成waitor加入队列中
- 原理
- release()
- 而release则表示将状态设置回去,也就是将资源释放,或者说将锁释放。
- 源码解析:
- 原理:
- 该方法取出了当前节点的next引用,然后对其线程(Node)进行了唤醒,这时就只有一个或合理个数的线程被唤醒,被唤醒的线程继续进行对资源的获取与争夺。
- 而release则表示将状态设置回去,也就是将资源释放,或者说将锁释放。
- private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException
- 原理
- 原理
- public final void acquireShared(int arg)
- 调用该方法能够以共享获取状态,
- 逻辑:
- 尝试获取共享状态;
- 调用tryAcquireShared(arg)方法获得
- 获取失败加入syn队列
- 以共享锁模式加入队列
- 循环退出条件
- 当前节点的前驱节点是否为头节点且能够获取共享状态
- 与独占锁的退出机制一致
- 判断共享状态成功
- 将当前节点设为头节点,判断后继节点是否为共享模式
- 如果是,则唤醒该线程,对其进行唤醒操作,也就是同时激发多个线程并发的运行
- 获取状态失败
- 使用LockSupport.park(this);将线程从调度器上摘下,进入休休眠状态
- 尝试获取共享状态;
- public final boolean releaseShared(int arg)
- 每次获取共享状态acquireShared都会操作状态,同样在共享锁释放的时候,也需要将状态释放。
- acquire(intr arg)
- 总结:
- 这篇文章,我们从ReentrantLock出发,完整的分析了AQS独占功能的API及内部实现。
- 总的来说,思路其实并不复杂,还是使用的标志位+队列的方式,记录获取锁、竞争锁、释放锁等 一系列锁的状态,或许用更准确一点的描述的话,应该是使用的标志位+队列的方式,记录锁,竞争,释放等一系列独占的状态,因为站在AQS的层面state可以表示锁,也可以表示其他状态,它并不关心它的子类把它变成一个什么工具类,而只是提供了一套维护一个独占状态。
- 甚至,最准确的是AQS只是维护了一个状态,因为,别忘了,它还有一套共享状态的API,所以,AQS只是维护一个状态,一个控制各个线程何时可以访问的状态,它只对状态负责,而这个状态表示什么含义,由子类自己去定义。