引言

共享锁的释放的主要方法doReleaseShared()已经在共享所的获取中简单的分析了一下,可见共享锁触发唤醒后记节点的行为有两处:第一,当前节点成功获取共享锁后,第二,当前节点释放共享锁后。
下面分析共享锁释放的主要逻辑代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 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)) {
//释放锁
doReleaseShared();
return true;
}
return false;
}

其中的tryReleaseShared(arg)由子类实现,Semaphore的内部类Sync中的实现为:

1
2
3
4
5
6
7
8
9
10
11
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}

看源码比较简单,for循环+CAS+状态修改。
方法: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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 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) {
int ws = h.waitStatus;
//如果头节点的状态时SIGNAL,则需要唤醒其后继节点
if (ws == Node.SIGNAL) {
//设置节点状态为0,失败则从新开始循环
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//设置成功后,唤醒后继节点
unparkSuccessor(h);
} else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
//将头节点状态设置为PROPAGATE
continue; // loop on failed CAS
}
//如果头节点改变了,继续循环
if (h == head) // loop if head changed
break;
}
}
/**
* Wakes up node's successor, if one exists.
* 唤醒node的后继节点(如果存在的话)。
* 释放锁和取消获取锁时被调用
*
* @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.
* 如果状态为负,清除预期信号,如果此操作失败或状态被等待线程更改,则没有问题。
* CANCELLED = 1
* 0
* SIGNAL = -1
* CONDITION = -2
* PROPAGATE = -3
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 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.
* Thread to unpark被保存在后续节点中,它通常只是下一个节点,
* 但是,如果取消或明显为空,则从tail向后遍历以找到实际的非取消后继。
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//从后向前遍历节点,最后s为离当前节点最近的需要被唤醒的节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//唤醒操作
if (s != null)
LockSupport.unpark(s.thread);
}

这里面比较复杂点在于waitStatus状态的判断

  • ws值为Node.SIGNAL,则说明后继节点需要唤醒
  • ws为0是指当前队列的最后一个节点成为了头节点

唤醒头节点的后一个节点的效率很高,因为会有多个线程同时执行唤醒头节点的下一个节点。