引言

AQS(AbstractQueuedSynchronizer)是Java并发工具的基础,采用乐观锁,通过CAS与自旋轻量级的获取锁。
下面简单分析一下他的核心实现:
###核心属性
锁相关属性:

1
2
3
4
5
6
7
8
9
10
/**
* The synchronization state.
* 同步状态,0代表锁没有被占用,大于0代表锁被占用且表示锁被重入的次数
*/
private volatile int state;
/**
*存储当前获取锁的线程,继承自AbstractOwnableSynchronizer
*/
private transient Thread exclusiveOwnerThread;

同步队列相关属性:

1
2
3
4
5
6
7
8
9
/**
* 对首,只是一个标志位,不存储线程
*/
private transient volatile Node head;

/**
* 队尾,新增加的等待线程入队节点
*/
private transient volatile Node tail;

Node节点中重要属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
*当前节点所代表的线程
*/
volatile Thread thread;

// 双向链表,每个节点需要保存自己的前驱节点和后继节点的引用
volatile Node prev;
volatile Node next;

// 线程所处的等待锁的状态,初始化时,该值为0
volatile int waitStatus;
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;

/**
* 用于条件队列与共享锁
*/
Node nextWaiter;

状态

AQS中的状态,代表锁是否被获取,如果为0,则没有被任何线程获取,如果是独占锁的话,值为1,如果是共享锁,则值为持有锁的线程的数量。
当讨论独占锁时,除了获取锁之外,还要记录一下获取到锁的线程,所以有了类AbstractOwnableSynchronizer中的exclusiveOwnerThread属性。

队列

AQS中的队列是双向链表,依赖于Node节点中的prev和next属性,队列中存储了等待获取锁的集合。队列中有head和tail节点,头节点不存储等待锁线程。
如图:
同步队列.png

CAS

以前也好好看过CAS操作,主要依赖于Unsafe类中的各种方法,保证只有一个线程能修改值,如果失败则通过for循环不断重试,直到修改成功,入等待节点入队列的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}

参考:
https://segmentfault.com/a/1190000016058789