AQS是JUC锁框架中最重要的类,通过它来实现独占锁和共享锁的。
本章是对AbstractQueuedSynchronizer源码的完全解析,分为四个部分介绍:
- CLH队列即同步队列:储存着所有等待锁的线程
- 独占锁
- 共享锁
- Condition条件
注: 还有一个AbstractQueuedLongSynchronizer类,它与AQS功能和实现几乎一样,唯一不同的是AQLS中代表锁被获取次数的成员变量state类型是long长整类型,而AQS中该成员变量是int类型。
一. CLH队列(线程同步队列)
因为获取锁是有条件的,没有获取锁的线程就要阻塞等待,那么就要存储这些等待的线程。
在AQS中我们使用CLH队列储存这些等待的线程,但它并不是直接储存线程,而是储存拥有线程的node节点。所以先介绍重要内部类Node。
static final class Node {
// 共享模式的标记
static final Node SHARED = new Node();
// 独占模式的标记
static final Node EXCLUSIVE = null;
// waitStatus变量的值,标志着线程被取消
static final int CANCELLED = 1;
// waitStatus变量的值,标志着后继线程(即队列中此节点之后的节点)需要被阻塞.(用于独占锁)
static final int SIGNAL = -1;
// waitStatus变量的值,标志着线程在Condition条件上等待阻塞.(用于Condition的await等待)
static final int CONDITION = -2;
// waitStatus变量的值,标志着下一个acquireShared方法线程应该被允许。(用于共享锁)
static final int PROPAGATE = -3;
// 标记着当前节点的状态,默认状态是0, 小于0的状态都是有特殊作用,大于0的状态表示已取消
volatile int waitStatus;
// prev和next实现的双向链表
volatile Node prev;
volatile Node next;
// 该节点拥有的线程
volatile Thread thread;
// 可能有两种作用:
// 1. 表示下一个在Condition条件上等待的节点
// 2. 表示是共享模式或者独占模式,注意第一种情况节点一定是共享模式
Node nextWaiter;
// 是不是共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
// 返回前一个节点prev,如果为null,则抛出NullPointerException异常
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 用于创建链表头head,或者共享模式SHARED
Node() {
}
// 使用在addWaiter方法中
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
// 使用在Condition条件中
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
重要的成员属性:
- waitStatus: 表示当前节点的状态,默认状态是0。总共有五个值CANCELLED、SIGNAL、CONDITION、PROPAGATE以及0。
- prev和next:记录着当前节点前一个节点和后一个节点的引用。
- thread:当前节点拥有的线程。当拥有锁的线程释放锁的时候,可能会调用LockSupport.unpark(thread),唤醒这个被阻塞的线程。
- nextWaiter:如果是SHARED,表示当前节点是共享模式,如果是null,当前节点是独占模式,如果是其他值,当前节点也是独占模式,不过这个值也是Condition队列的下一个节点。
注意:通过Node我们可以实现两个队列,一是通过prev和next实现CLH队列(线程同步队列,双向队列),二是nextWaiter实现Condition条件上的等待线程队列(单向队列),后一个我们在Condition中介绍。
1.2 操作CLH队列
1.2.1 存储CLH队列
// 头队列
private transient volatile Node head;
//尾队列
private transient volatile Node tail;
1.2.2 设置CLH队列头head
/**
* CAS方法设置队列头,仅在enq方法中使用
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
这个方法只在enq方法中调用,通过CAS函数设置head值,保证多线程安全
/**
* 重新设置队列头,仅在acquire中使用
*/
private void setHead(Node node) {
head = node;
// 线程已经没有意义了,因为线程已经获取到锁了
node.thread = null;
// 前一个节点已经没有意义了
node.prev = null;
}
这个方法只在acquire系列的方法中调用,重新设置head,表示移除一些等待线程节点。
1.2.3 设置CLH队列尾tail
/**
* 通过CAS设置tail,仅在enq中使用
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
这个方法只在enq方法中调用,通过CAS函数设置tail值,保证多线程安全
1.2.4 将一个节点插入到CLH队列尾
/**
* 向队尾插入新节点,如果队列没有初始化,就先初始化。返回原先队尾节点
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// t为null,表示队列为空,需先初始化队列
if (t == null) {
// 采用CAS函数即原子操作方式,设置队列头head值
// 如果成功,再将head赋值给队尾tail。
// 如果失败,表示head已经被其他线程使用,那么就那么就进入下一次循环
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 新添加的node节点的前一个节点prev指向原来的队列尾tail
node.prev = t;
// 采用CAS函数即原子操作方式,设置新队列尾tail值。
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
这个方法向CLH队列尾插入一个新节点,如果队列为空,就先创建队列再插入新节点,返回老的队列尾节点。
1.2.5 将当前线程添加到CLH队列尾
// 通过给定的模式mode(独占或者共享)为当前线程创建新节点,并插入队列中
private Node addWaiter(Node mode) {
// 为当前线程创建新的节点
// 这个构造方法前面介绍过,用来指定是独占还是共享
Node node = new Node(Thread.currentThread(), mode);
// 尝试enq的快速路径;失败时备份到完全enq, 原注释(实际就是截取enq常规代码,提高效率)
Node pred = tail;
// 如果队列已经创建则向队尾加入新的节点
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果没有创建,通过enq方法创建队列,并插入新的节点。
//(一般都创建了,所以把上部分代码提取出来)
enq(node);
return node;
}
为当前线程创建一个新节点,再插入到CLH队列尾,返回新创建的节点。
附录
package java.util.concurrent.locks;
import sun.misc.Unsafe;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
private static final long serialVersionUID = 7373984972572414691L;
protected AbstractQueuedSynchronizer() { }
static final class Node {
// 共享模式的标记
static final Node SHARED = new Node();
// 独占模式的标记
static final Node EXCLUSIVE = null;
// waitStatus变量的值,标志着线程被取消
static final int CANCELLED = 1;
// waitStatus变量的值,标志着后继线程(即队列中此节点之后的节点)需要被阻塞.(用于独占锁)
static final int SIGNAL = -1;
// waitStatus变量的值,标志着线程在Condition条件上等待阻塞.(用于Condition的await等待)
static final int CONDITION = -2;
// waitStatus变量的值,标志着下一个acquireShared方法线程应该被允许。(用于共享锁)
static final int PROPAGATE = -3;
// 标记着当前节点的状态,默认状态是0, 小于0的状态都是有特殊作用,大于0的状态表示已取消
volatile int waitStatus;
// prev和next实现一个双向链表
volatile Node prev;
volatile Node next;
// 该节点拥有的线程
volatile Thread thread;
// 可能有两种作用:1. 表示下一个在Condition条件上等待的节点
// 2. 表示是共享模式或者独占模式,注意第一种情况节点一定是共享模式
Node nextWaiter;
// 是不是共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
// 返回前一个节点prev,如果为null,则抛出NullPointerException异常
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 用于创建链表头head,或者共享模式SHARED
Node() {
}
// 使用在addWaiter方法中
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
// 使用在Condition条件中
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
// CLH队列头
private transient volatile Node head;
// CLH队列尾
private transient volatile Node tail;
// 用来记录当前锁被获取的次数,当state==0,表示还没有被任何线程获取
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
// 采用CAS函数。比较并交换函数,它是原子操作函数;即,通过CAS操作的数据都是以原子方式进行的
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// Queuing utilities
static final long spinForTimeoutThreshold = 1000L;
// 向队列尾插入新节点,如果队列没有初始化,就先初始化。返回原先的队列尾节点
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// t为null,表示队列为空,先初始化队列
if (t == null) {
// 采用CAS函数即原子操作方式,设置队列头head值。
// 如果成功,再将head值赋值给链表尾tail。如果失败,表示head值已经被其他线程,那么就进入循环下一次
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 新添加的node节点的前一个节点prev指向原来的队列尾tail
node.prev = t;
// 采用CAS函数即原子操作方式,设置新队列尾tail值。
if (compareAndSetTail(t, node)) {
// 设置老的队列尾tail的下一个节点next指向新添加的节点node
t.next = node;
return t;
}
}
}
}
// 通过给定的模式mode(独占或者共享)为当前线程创建新节点,并插入队列中
private Node addWaiter(Node mode) {
// 为当前线程创建新的节点
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
// 如果队列已经创建,就将新节点插入队列尾。
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果队列没有创建,通过enq方法创建队列,并插入新的节点。
enq(node);
return node;
}
// 重新设置队列头head,它只在acquire系列的方法中调用
private void setHead(Node node) {
head = node;
// 线程也没有意义了,因为该线程已经获取到锁了
node.thread = null;
// 前一个节点已经没有意义了
node.prev = null;
}
// 唤醒node节点的下一个非取消状态的节点所在线程(即waitStatus<=0)
private void unparkSuccessor(Node node) {
// 获取n