重入锁是如何实现的?
# 什么是重入锁?
ReentrantLock,重入锁,是JDK5中添加在并发包下的一个高性能的工具。顾名思义,ReentrantLock支持同一个线程在未释放锁的情况下重复获取锁。
既然已经有了元老级的synchronized,而且synchronized也支持重入,为什么Doug Lea还要专门写一个ReentrantLock呢?
这是因为:当存在大量线程竞争锁时,多数情况下ReentrantLock的性能优于synchronized。
因为在JDK6中对synchronized做了优化,在锁竞争不激烈的时候,多数情况下锁会停留在偏向锁和轻量级锁阶段,这两个阶段性能是很好的。当存在大量竞争时,可能会膨胀为重量级锁,性能下降,此时的ReentrantLock应该是优于synchronized的。
# ReentrantLock - 重入锁加锁
// acquires的值是1
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取state的值
int c = getState();
// 如果state的值等于0,表示当前没有线程持有锁
// 尝试将state的值改为1,如果修改成功,则成功获取锁,并设置当前线程为持有锁的线程,返回true
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// state的值不等于0,表示已经有其他线程持有锁
// 判断当前线程是否等于持有锁的线程,如果等于,将state的值+1,并设置到state上,获取锁成功,返回true
// 如果不是当前线程,获取锁失败,返回false
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
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
# ReentrantLock - 重入锁解锁
public void unlock() {
// 调用AQS的release方法释放资源
sync.release(1);
}
1
2
3
4
2
3
4
public final boolean release(int arg) {
// tryRelease也是模板方法,在Sync中实现
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
2
3
4
5
6
7
8
9
10
11
12
protected final boolean tryRelease(int releases) {
// 将state的值-1,如果-1之后等于0,释放锁成功
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
编辑 (opens new window)
上次更新: 2022/05/19, 21:26:01