独占式获得锁

独占式下的顶层函数为acquire(),首先调用tryAcquire()函数获得锁,如果获取不到锁,则将线程加入到队列中。

1
2
3
4
5
6
7
public final void acquire(int arg) {
if (!tryAcquire(arg) && //尝试获得锁
//将线程加入到对列中,循环获取资源,并在一定次数后阻塞
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//在阻塞过程中线程可能被中断,忽略中断,在获取到锁后再进行中断。
selfInterrupt();
}

addWaiter()将线程加入到阻塞队列中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private Node addWaiter(Node mode) {

//将建一个队列节点,并设置该节点为独占模式
Node node = new Node(Thread.currentThread(), mode);
// 尝试快速插入到队列中,失败则调用enq函数
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}

将node节点放入到对尾中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//如果队列为空,则设置对尾节点为空,并将队首指向对尾
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
//将该节点的前趋节点设置为当前对列尾节点
node.prev = t;
//将对尾节点设置为尾节点
if (compareAndSetTail(t, node)) {
//将原尾节点的后继指向该节点
t.next = node;
return t;
}
}
}
}

acquireQueued()通过自旋的方式获取同步状态,在需要阻塞线程的时候阻塞线程。

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
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前节点的前趋节点
final Node p = node.predecessor();
//判断前趋节点是否是头节点,如果是,再次掉用tryAcquire尝试获取锁
if (p == head && tryAcquire(arg)) {
//如果获取到锁,则将当前节点设置成头节点
setHead(node);
//在设置头节点时,node的前趋节点已经设置成null,所以这里只将p的next设置成null,p节点将不在队列中
p.next = null; // help GC
failed = false;
return interrupted;
}
//判断是否应该被阻塞,如果应该被阻塞则阻塞该节点
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
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//如果当前节点的前趋节点的状态为SIGNAL则阻塞当前节点
return true;
if (ws > 0) {
do {
//如果当前节点的前趋节点状态>0(CANCELLED),那就一直往前找
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//如果前趋节点为正常状态,则修改前趋节点的状态为SIGNAL,用于线程结束时,唤起下一个节点
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}

挂起线程

1
2
3
4
5
6
private final boolean parkAndCheckInterrupt() {
//阻塞当前线程
LockSupport.park(this);
//当前线程可被中断,返回该线程的中断状态,在该线程获取到锁后,再进行中断
return Thread.interrupted();
}

独占式释放锁

1
2
3
4
5
6
7
8
9
10
public final boolean release(int arg) {
//尝试获释放锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//修改头节点的状态
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);

//获取头节点的下一个节点
Node s = node.next;

//判断s线程是否是null,或线程状态是否是>0(比如CANCELLED状态 代表取消),如果为true,则遍历队列,找到应该被唤醒的线程。
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//唤醒线程
LockSupport.unpark(s.thread);
}