本文简单了解AQS(AbstractQueuedSynchronizer)
之前我们用Synchronized来实现同步,它是通过monitor的monitorenter和monitorexit指令来获取锁和释放锁的,追究到底层其实是jvm提供的lock和unlock指令来实现加锁和释放锁的。ASQ是实现同步组件(锁)的关键,那么他是如何做的呢。
AQS简介
AQS 全称 AbstractQueuedSynchronizer 翻译为队列同步器,同步器是用来构建锁和其他同步组件的基础框架,它的实现主要依赖一个int成员变量(state)来表示同步状态以及通过一个FIFO队列构成等待队列。同时提供共享锁和独占锁两种锁,这样便可实现不同类型的同步组件。
AQS的子类被推荐定义为同步组件(锁)的内部类,它的子类必须重写AQS的几个protected修饰的用来改变同步状态的方法,其他方法主要是实现了排队和阻塞机制。状态的获取和更新使用getState,setState以及compareAndSetState这三个方法。
总结来说:AQS代替我们去和底层打交道,我们只要使用同步组件(锁)即可。
看一下AQS类结构
两个内部类:
- ConditionObject
和锁绑定的等待通知组件。前篇文章,我们使用ReentrantLock.newCondition()创建的Condition对象其实就是这个。
- Node
节点对象,保存当前线程的线程引用、线程状态、前驱节点和后驱节点
其他属性:
主要看这三个属性
- head 头结点
- tail 尾结点
- state 同步状态
Node节点状态
Node类存在以下属性:
- volatile int waitStatus //节点状态
- volatile Node prev //当前节点/线程的前驱节点
- volatile Node next; //当前节点/线程的后继节点
- volatile Thread thread;//加入同步队列的线程引用
- Node nextWaiter;//等待队列中的下一个节点
节点状态简介:
1、CANCELLED = 1
由于超时或中断,节点从同步队列中取消。节点一旦被取消了就不会再改变状态。
注意:
①中断只是在线程中打一个中断标志,他不会停止或杀死线程,所以这里需要AQS对中断线程做出反应(也就是抛异常),
此时同步代码不会执行了,会执行catch。
②ASQ管理的是同步队列这是一个双向链表,当节点状态为1时,会从同步队列断开,状态当然无法改变。
2、int SIGNAL = -1
入队时节点默认是0,自旋时通过cas使得节点状态为-1,只有当节点状态小于0时才会调用LockSuport.unpack()
3、int CONDITION = -2
当前节点进入等待队列中
4、 int PROPAGATE = -3
表示下一次共享式同步状态获取将会无条件传播下去
5、int INITIAL = 0;
初始状态
同步队列
AQS通过同步状态state和一个先进先出的队列来管理线程的,这里介绍一下同步队列结构。新增节点通过尾插法的方式加入队列。head、tail分别指向同步队列的首节点和尾结点。
同步状态
对于同步状态:独占锁和共享锁是不同的。
- 对于独占锁:state既可以作为同步状态的标志(等于0即可以获取锁,大于0即阻塞)
- 对于共享锁:state可以作为同步状态标志为,但不可同时作为重入计数器,所以每个获取共享所得线程都会维护一个HoldCounter来记录重入次数。
等待队列
之前Node节点有一个nextWaiter属性,就是用来记录等待队列的下一个节点的。
等待队列结构:
如果在等待队列中被唤醒,会尾插法插入同步队列。