AbstractQueuedSynchronizer笔记

前言

一系列并发API的基础。

正文

设计

  • 将高层次状态的表示与转换经过映射逻辑转换为低层次状态的表示与转换(状态模式),可以理解为AQS就是一个手表,指针位置这种上层状态的转变就是我们要实现的上层逻辑,他会映射到齿轮发条的状态转变这种底层状态的转变,也就是底层逻辑
  • 部分逻辑留给子类实现,自己实现通用逻辑(模板方法模式)

概念

  • 状态:用来实现高级抽象状态(获取到锁,获取到资源)的低级状态
  • 等待队列:当前线程尝试修改底层状态,失败后会进入等待队列(从队尾进入)

实现

状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;

行为

获取(独占式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// “获取”类型操作(比如获取锁)的入口
/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void (int arg) {
if (!tryAcquire(arg) && // ① 先尝试进行获取操作(也就是修改内部的状态),如果成功则不会进行后续操作
acquireQueued(
addWaiter(Node.EXCLUSIVE), arg) // ② 如果上一步获取失败(也就是修改内部状态失败)则先尝试将当前线程构造为等待队列的节点从队尾入队
) // ③ 然后再尝试进行获取操作
selfInterrupt(); // ④ 如果在上一步的进队尝试获取操作返回了true,并且过程中发生了中断,则在此进行中断
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 上边 ① 处:tryAcquire(arg) 留给子类实现
/**
* Attempts to acquire in exclusive mode. This method should query
* if the state of the object permits it to be acquired in the
* exclusive mode, and if so to acquire it.
*
* <p>This method is always invoked by the thread performing
* acquire. If this method reports failure, the acquire method
* may queue the thread, if it is not already queued, until it is
* signalled by a release from some other thread. This can be used
* to implement method {@link Lock#tryLock()}.
*
* <p>The default
* implementation throws {@link UnsupportedOperationException}.
*
* @param arg the acquire argument. This value is always the one
* passed to an acquire method, or is the value saved on entry
* to a condition wait. The value is otherwise uninterpreted
* and can represent anything you like.
* @return {@code true} if successful. Upon success, this object has
* been acquired.
* @throws IllegalMonitorStateException if acquiring would place this
* synchronizer in an illegal state. This exception must be
* thrown in a consistent fashion for synchronization to work
* correctly.
* @throws UnsupportedOperationException if exclusive mode is not supported
*/
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 给出一个例子,如子类: java.util.concurrent.locks.ReentrantLock.Sync 的实现就是尝试修改 AbstractQueuedSynchronizer 中的状态
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 上边 ② 处:执行获取操作失败(也就是子类实现中修改状态失败)则先尝试将当前线程构造为等待队列的节点从队尾入队
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);

// Ⅰ. 如果当前队尾不是空
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred; // 则将当前队尾设置为新插入节点的前驱

// Ⅱ. 然后再尝试用CAS操作将构造好的新节点设置成新的队尾
if (compareAndSetTail(pred, node)) {
pred.next = node; // 如果成功设置为了新队尾则让原来的队尾节点的后继设置为新插入的节点
return node; // Ⅲ. 并返回新插入节点
}
}

// Ⅳ. 如果当前队尾为空或者上边的操作没有在 Ⅲ 处返回
enq(node); // 则用循环CAS的方式尝试 Ⅱ 的逻辑,之所以不一上来就用循环CAS做,是为了先乐观的尝试一次,如果一次性就成功了,就不需要反复重试了,这样就能免除反复重试的消耗
return node;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 接上边 Ⅱ 处:如果当前队尾为空或者上边的操作没有在 Ⅲ 处返回 则用循环CAS的方式再尝试上边代码的逻辑(注意和上边有些不同)
/**
* 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())) // 则新建一个节点用CAS设置为头节点
tail = head; // 如果头节点设置成功了则将其同时设置为尾节点

// 否则(也就是尾节点不为空)
} else {
node.prev = t; // 将给定节点的前驱设置为尾节点
if (compareAndSetTail(t, node)) { // 然后用CAS设置当前给定节点为新的尾节点
t.next = node; // 如果成功设置为了新队尾则让原来的队尾节点的后继设置为新插入的节点
return t; // 并返回新插入节点
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 将上一步从尾部新插入的节点进行尝试获取操作
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); // 获得当前节点的前驱
if (p == head && tryAcquire(arg)) { // 如果前驱是头节点,并且当前获取操作成功
setHead(node); // 那么就把当前节点设置为新的头节点
p.next = null; // help GC
failed = false;
return interrupted;
}

// 如果上面获取操作失败,那么就 先检查是否需要 park 当前节点,如果需要则进行 park
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 上边的 shouldParkAfterFailedAcquire 里面并不进行真正的 park,而是将传入的前驱节点设置为 SIGNAL 也就是前驱在释放后需要通知后边的节点
/**
* Checks and updates status for a node that failed to acquire.
* Returns true if thread should block. This is the main signal
* control in all acquire loops. Requires that pred == node.prev.
*
* @param pred node's predecessor holding status
* @param node the node
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus; // 获得传入的前驱节点的状态
if (ws == Node.SIGNAL) // 假如传入的前驱节点的状态为 SIGNAL 则直接返回 true,true就意味着当前节点需要等待,SIGNAL 的意思是当前节点进行了释放操作后需要通知后继节点
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;

// 如果传入的前驱节点的状态大于 0 也就是 CANCELLED 这个状态(因为只有这个状态是大于0的)
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
大专栏  AbstractQueuedSynchronizer笔记 node.prev = pred = pred.prev; // 则将当前节点的前驱设置为前驱的前驱,也就是将传入的前驱节点摘掉
} while (pred.waitStatus > 0);
pred.next = node;
}

// 否则当前传入的前驱节点的状态要莫是 0 要莫是 PROPAGATE
else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); // 用CAS方式将当前传入的前驱节点的状态设置为 SIGNAL,也就是前驱释放了需要通知当前节点
}
return false;
}
1
2
3
4
5
6
7
8
9
10
// 真正执行 park
/**
* Convenience method to park and then check if interrupted
*
* @return {@code true} if interrupted
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}

释放(独占式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// "释放"操作,
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
// tryRelease 交给子类实现
if (tryRelease(arg)) { // 如果释放操作成功了
Node h = head; // 等待队列头节点
if (h != null && h.waitStatus != 0) // 如果头节点存在,并且其状态不为 0
unparkSuccessor(h); // 那么就 unparkSuccessor
return true;
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 唤醒给定节点的后继节点
/**
* Wakes up node's successor, if one exists.
*
* @param node the node
*/
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;

// 如果给定的当前节点的状态是小于0的,也就是不是 CANCELLED 或者 0,也就是“正常”的状态
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 那么就用CAS方式把给定的当前节点的状态设置为 0
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next; // 给定节点的后继节点

// 如果给定节点的后继节点是空或者其状态是 CANCELLED
if (s == null || s.waitStatus > 0) {
s = null; // 那么就将给定节点的后继节点设置为空

// 然后从等待队列的尾部向前找,每次找到状态小于 0 的节点就将其设置为给定节点的后继,直到到了给定后继节点为止,也就是找到给定节点的后继节点后边第一个“正常”状态的节点设置为后继节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}

// 如果经过了上面的操作到了这里给定节点的后继节点不是空的话
if (s != null)
LockSupport.unpark(s.thread); // 则 unpark 现在的定节点的后继节点中存储的 thread 对象
}

获取(共享式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 共享式获取
/**
* Acquires in shared mode, ignoring interrupts. Implemented by
* first invoking at least once {@link #tryAcquireShared},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquireShared} until success.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquireShared} but is otherwise uninterpreted
* and can represent anything you like.
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) // tryAcquireShared 交给子类实现,尝试做获取操作
doAcquireShared(arg); // 假如上面的获取操作失败了,则 doAcquireShared ,在里面是如等待队列并根据条件尝试再获取
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* Acquires in shared uninterruptible mode.
* @param arg the acquire argument
*/
private void doAcquireShared(int arg) {
// 以共享模式构建当前等待队列节点,然后将当前节点从队尾入队
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;

// 反复
for (;;) {

// 获得当前节点的前驱节点
final Node p = node.predecessor();

// 如果当前节点的前驱节点是头节点
if (p == head) {

// 再尝试进行获取操作
int r = tryAcquireShared(arg);

// 如果获取操作完毕后仍有可用资源用于获取
if (r >= 0) {

// 将当前节点设置为新的头节点
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}

// 如果获取操作失败了,则 shouldParkAfterFailedAcquire 里面并不进行真正的 park,而是将传入的前驱节点设置为 SIGNAL 也就是前驱在释放后需要通知后边的节点,这个逻辑和互斥型“获取”是一致的
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) // 如果 shouldParkAfterFailedAcquire 成功了,也就是设置前驱节点为 SIGNAL (释放后需要通知后边节点) 这个状态成功了
interrupted = true; // 中断过就位 true
}
} finally {
if (failed)
cancelAcquire(node);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* Sets head of queue, and checks if successor may be waiting
* in shared mode, if so propagating if either propagate > 0 or
* PROPAGATE status was set.
*
* @param node the node
* @param propagate the return value from a tryAcquireShared
*/
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node); // 将当前节点设置为新的头节点

/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
// 然后,判断传入的 propagate(也就是剩余资源数)是否大于0,或者原头节点为空或原头节点状态小于0(意味着状态为SIGNAL或PROPAGATE)或者经过上边的设置头节点后的新的头节点为空或者经过上边的设置头节点后的新的头节点状态小于0(意味着状态为SIGNAL或PROPAGATE)
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {

// 获得当前节点的后继节点
Node s = node.next;

// 如果后继节点为空或者后继节点为共享类型的节点
if (s == null || s.isShared())

// 那么就 doReleaseShared 这里面是做了保证后继节点的后继节点也能被唤醒,唤醒了就能参与“获取”操作(因为共享型的获取不只自己能获取,也要让后边的人也能获取才行,这样才是共享的)其实复用了“释放”操作得一部分逻辑
doReleaseShared();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* Release action for shared mode -- signals successor and ensures
* propagation. (Note: For exclusive mode, release just amounts
* to calling unparkSuccessor of head if it needs signal.)
*/
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
// 反复
for (;;) {
Node h = head;
if (h != null && h != tail) {
// 如果头节点的状态为 SIGNAL(也就是要自己等待然后去尝试唤醒后继节点)
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
// 如果自己等待失败了就反复重试
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
// 直到自己成功变为等待状态了,就唤醒后继节点
unparkSuccessor(h);
}

// 头节点为等待状态(ws == 0)时,将自己设置为 PROPAGATE (传播状态,这里就是在 setHeadAndPropagate 中调用 doReleaseShared 的目的)这个设置是反复重试直到成功的
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}

// 反复的条件:如果头节点发生过改变
if (h == head) // loop if head changed
break;
}
}

释放(共享式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Releases in shared mode. Implemented by unblocking one or more
* threads if {@link #tryReleaseShared} returns true.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryReleaseShared} but is otherwise uninterpreted
* and can represent anything you like.
* @return the value returned from {@link #tryReleaseShared}
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) { // tryReleaseShared 交给子类实现
doReleaseShared(); // 如果尝试释放成功了则 doReleaseShared ?
return true;
}
return false;
}

参考资料

AQS的基本原理

源码|并发一枝花之ReentrantLock与AQS(1):lock、unlock

猜你喜欢

转载自www.cnblogs.com/liuzhongrong/p/12371434.html