AQS简介

279 阅读3分钟

本文简单了解AQS(AbstractQueuedSynchronizer)

上一章 下一章

之前我们用Synchronized来实现同步,它是通过monitor的monitorenter和monitorexit指令来获取锁和释放锁的,追究到底层其实是jvm提供的lock和unlock指令来实现加锁和释放锁的。ASQ是实现同步组件(锁)的关键,那么他是如何做的呢。

AQS简介

AQS 全称 AbstractQueuedSynchronizer 翻译为队列同步器,同步器是用来构建锁和其他同步组件的基础框架,它的实现主要依赖一个int成员变量(state)来表示同步状态以及通过一个FIFO队列构成等待队列。同时提供共享锁和独占锁两种锁,这样便可实现不同类型的同步组件。

AQS的子类被推荐定义为同步组件(锁)的内部类,它的子类必须重写AQS的几个protected修饰的用来改变同步状态的方法,其他方法主要是实现了排队和阻塞机制。状态的获取和更新使用getState,setState以及compareAndSetState这三个方法

总结来说:AQS代替我们去和底层打交道,我们只要使用同步组件(锁)即可。


看一下AQS类结构

两个内部类:

image.png

  • ConditionObject
和锁绑定的等待通知组件。前篇文章,我们使用ReentrantLock.newCondition()创建的Condition对象其实就是这个。
  • Node
节点对象,保存当前线程的线程引用、线程状态、前驱节点和后驱节点

其他属性:

image.png

主要看这三个属性

  • 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分别指向同步队列的首节点和尾结点。

image.png


同步状态

对于同步状态:独占锁和共享锁是不同的。

  • 对于独占锁:state既可以作为同步状态的标志(等于0即可以获取锁,大于0即阻塞)
  • 对于共享锁:state可以作为同步状态标志为,但不可同时作为重入计数器,所以每个获取共享所得线程都会维护一个HoldCounter来记录重入次数。

等待队列

之前Node节点有一个nextWaiter属性,就是用来记录等待队列的下一个节点的。

等待队列结构:

image.png

如果在等待队列中被唤醒,会尾插法插入同步队列。