一:AQS原理简析
AQS是java中多数的同步器的实现基础,AQS维护同步器的同步状态。在AQS中维护同步器状态,主要是从以下的三个方面来维护的:
- 同步状态的管理
- 线程的唤醒/阻赛
- 线程等待队列的维护
二:同步状态的管理
AQS同步器定义了资源的访问方式:独占模式还是共享模式。AQS使用单个32位的int来维护同步状态的,并且暴露出以下的方法来更新获取这个状态:
/**
* 同步状态.
*/
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
/**
* 以原子的方式更新同步状态.
* 利用Unsafe类实现
*/
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
其中一些同步器对于state的变量的使用:
三:线程的唤醒和阻塞
我们知道在使用多线程时,常用的都是Object的wait(),notity()以及notityAll()来对线程进行阻塞和唤醒操作。在引入JUC这个并发包之后就有了LockSupport这个工具支持线程的操作,具体表现在锁的Condition中。
四:等待队列FIFO
AQS的FIFO队列采用的是CLH队列(CLH队列),FIFO遵循一个基本的先进先出的原则。
1.FIFO存取线程的步骤
(1)首先,线程请求资源时先判断锁表示(state)是否上锁。如果没有上锁执行(2),否则执行(3)
(2)如果当前线程请求时没有上锁,使用原子操作,占用所标识。
(3)如果已经上锁,将当前线程相关信息封装成一个Node节点
(4)将该Node节点存放在队列尾部
(5)如果占锁的线程已经释放锁,那么通知队列的第一个Node节点释放并且占有锁。
2.FIFO中节点的API
static final class Node {
// 共享模式结点
static final Node SHARED = new Node();
// 独占模式结点
static final Node EXCLUSIVE = null;
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
/**
* INITAL: 0 - 默认,新结点会处于这种状态。
* CANCELLED: 1 - 取消,表示后续结点被中断或超时,需要移出队列;
* SIGNAL: -1- 发信号,表示后续结点被阻塞了;(当前结点在入队后、阻塞前,应确保将其prev结点类型改为SIGNAL,以便prev结点取消或释放时将当前结点唤醒。)
* CONDITION: -2- Condition专用,表示当前结点在Condition队列中,因为等待某个条件而被阻塞了;
* PROPAGATE: -3- 传播,适用于共享模式。(比如连续的读操作结点可以依次进入临界区,设为PROPAGATE有助于实现这种迭代操作。)
*
* waitStatus表示的是后续结点状态,这是因为AQS中使用CLH队列实现线程的结构管理,而CLH结构正是用前一结点某一属性表示当前结点的状态,这样更容易实现取消和超时功能。
*/
volatile int waitStatus;
// 前驱指针
volatile Node prev;
// 后驱指针
volatile Node next;
// 结点所包装的线程
volatile Thread thread;
// Condition队列使用,存储condition队列中的后继节点
Node nextWaiter;
Node() {
}
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
}
3.节点的指向
Node节点的维护是用的一个双向链表,其中当前当前节点的状态一定是在前一个节点中某一个属性体现出了。这样就很容易实现Node节点的取消和删除功能(超时和响应中断的功能)