介绍
ReentrantLock和Synchronized一样都是可重入的。
ReentrantLock与Synchronized相比较而言。
优势
支持公平/非公平锁、支持可中断的锁、支持非阻塞的tryLock(可超时)、支持锁条件、可跨代码块使用(一个地方加锁,另一个地方解锁),总之比Synchronized更加灵活。
缺点
锁需要显示解锁、无法充分享用JVM内部性能提升带来的好处等等。
源码分析
ReentrantLock实现Lock接口。
Lock
1 | public interface Lock { |
标注代码分析
- 获取锁,如果锁无法获取,当前线程被阻塞,直到锁可以获取并获取成功为止。
- 在当前线程没有被中断的情况下获取锁。如果锁无法获取,当前线程被阻塞,阻塞获取锁或者中断。此处有抛出InterruptedException。
- 尝试获取锁。
- 有超时时间和抛出InterruptedException。在指定时间内获取锁或者线程中断。
ReentrantLock#Sync
1 | /** |
根据注释,可以知道公平锁和非公平锁继承Sync。使用AQS state表示当前持有锁的重入数量。
ReentrantLock#Sync#nonfairTryAcquire()
1 | /** |
标注代码分析
- AbstractQueuedSynchronizer#state作为持有锁的数量。相当于重入次数。
- 通过CAS设置state获取锁,AbstractQueuedSynchronizer#setExclusiveOwnerThread()设置当前线程为锁的持有者。
- 如果当前线程是锁的持有者,叠加重入次数。重新设置锁重入次数到AbstractQueuedSynchronizer#state。这也是重入锁的核心,如果当前线程已经持有锁,并且计数值c > 0,这就可以再次获取到锁。
ReentrantLock#Sync#tryRelease()
1 | protected final boolean tryRelease(int releases) { |
标注代码分析
- 锁持有线程重入次数c。
- 当前线程不是锁持有线程,抛出IllegalMonitorStateException。
- 锁持有线程重入次数 = 0,设置AbstractQueuedSynchronizer#exclusiveOwnerThread = null,AQS锁持有者是null。
ReentrantLock#NonfairSync
非公平锁1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* Sync object for non-fair locks
*/
final static class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
// 1
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 2
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
标注代码分析
- 非公平锁的核心代码,
compareAndSetState(0, 1)
CAS抢占0,如果这时锁的计数是0,0表示没有其他线程竞争,当前线程竞争锁成功,设置当前线程为锁的持有者。否则调用AbstractQueuedSynchronizer#acquire()。 - 调用Sync#nonfairTryAcquire()尝试获取锁。
ReentrantLock#FairSync
公平锁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/**
* Sync object for fair locks
*/
final static class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
// 1
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 2
if (c == 0) {
if (isFirst(current) &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {// 3
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
标注代码分析
- 根据注释说,公平锁只有在递归方法(重入锁)、非等待队列、队列第1个,才能获取到锁。
- AbstractQueuedSynchronizer#isFirst(current)根据注释
queue is empty or if the given thread is at the head of the queue
判断是否是队列中第1个。如果是队列中第1个线程且竞争到锁,设置AbstractQueuedSynchronizer#exclusiveOwnerThread作为锁的持有者。 - 当前线程是AbstractQueuedSynchronizer#锁的持有者,叠加重入计数。重入锁的核心,原来同ReentrantLock#Sync#nonfairTryAcquire()一样。
公平锁和ReentrantLock#Sync#nonfairTryAcquire()非公平锁获取锁的区别就在isFirst(current),公平锁需要判断是否是线程队列的第1个。
ReentrantLock通过sync来实现锁操作。
ReentrantLock构造方法
1 | /** |
默认是非公平锁。
ReentrantLock#getHoldCount()
1 | /** |
当前线程持有这锁的次数。通俗的说就是重入次数。