概述
ReentrantLock是基于AQS独占模式实现的一种可重入锁,与synchronized不同的是,ReentrantLock提供了公平锁和非公平锁的选择。其本质是基于操作系统的管程实现的。本文就分析一下ReentrantLock的实现原理,由于AQS在AQS-独占模式分析已经介绍过,所以涉及到AQS的地方不会过多介绍,本文会涉及到源码,文章会比较臃肿。
ReentrantLock整体结构
从上图可以看出:
- ReentrantLock实现了Lock接口
- ReentrantLock内部是基于Sync这个抽象类实现的,而Sync继承了AQS,重写了部分AQS的方法
- Sync有两个继承类,FairSync是实现公平锁的,而NonFairSync是实现非公平锁的
下面就分析一下每一块的代码
Sync抽象类
1 //继承AQS
2 abstract static class Sync extends AbstractQueuedSynchronizer {
3 private static final long serialVersionUID = -5179523762034025860L;
4 //子类实现加锁方法,因为有公平锁和非公平锁,所以这里没有实现
5 abstract void lock();
6 //该方法时tryLock方法调用的,tryLock方法是尝试获取锁,如果没有获取成功,就直接返回
7 //不会阻塞,非公平的方式,直接竞争锁
8 final boolean nonfairTryAcquire(int acquires) {
9 final Thread current = Thread.currentThread();
10 int c = getState();
11 //该state是AQS中的,如果为0表示没有别的线程占有锁
12 if (c == 0) {
13 //通过CAS加锁
14 if (compareAndSetState(0, acquires)) {
15 //加锁成功,将持有锁的线程改成当前线程
16 setExclusiveOwnerThread(current);
17 return true;
18 }
19 }
20 //如果持有锁的线程就是当前线程,就是可重入锁,直接修改状态
21 else if (current == getExclusiveOwnerThread()) {
22 int nextc = c + acquires;
23 if (nextc < 0) // overflow
24 throw new Error("Maximum lock count exceeded");
25 setState(nextc);
26 return true;
27 }
28 //否则加锁失败,直接返回
29 return false;
30 }
31 //释放锁
32 protected final boolean tryRelease(int releases) {
33 int c = getState() - releases;
34 if (Thread.currentThread() != getExclusiveOwnerThread())
35 throw new IllegalMonitorStateException();
36 boolean free = false;
37 if (c == 0) {
38 free = true;
39 setExclusiveOwnerThread(null);
40 }
41 setState(c);
42 return free;
43 }
44 //判断当前线程是不是持有锁的线程
45 protected final boolean isHeldExclusively() {
46 // While we must in general read state before owner,
47 // we don't need to do so to check if current thread is owner
48 return getExclusiveOwnerThread() == Thread.currentThread();
49 }
50 //在AQS中的创建ConditionObject对象,保存等待线程
51 final ConditionObject newCondition() {
52 return new ConditionObject();
53 }
54
55 final Thread getOwner() {
56 return getState() == 0 ? null : getExclusiveOwnerThread();
57 }
58
59 final int getHoldCount() {
60 return isHeldExclusively() ? getState() : 0;
61 }
62
63 final boolean isLocked() {
64 return getState() != 0;
65 }
66
67 private void readObject(java.io.ObjectInputStream s)
68 throws java.io.IOException, ClassNotFoundException {
69 s.defaultReadObject();
70 setState(0); // reset to unlocked state
71 }
72 }
Sync抽象类总结
- 该类主要实现了一个非公平的方式尝试获取锁,如果成功,ok,如果失败,直接返回,不会阻塞或者空转等待
- 实现了一个释放锁的方法,该方法是公平锁和非公平锁公用的方法
- 剩下的都是一些判断的方法,很简单
NonFairSync类分析
1 static final class NonfairSync extends Sync {
2 private static final long serialVersionUID = 7316153563782823691L;
3 //实现加锁方法,ReentrantLock默认就是这个方法,是一个非公平锁
4 final void lock() {
5 if (compareAndSetState(0, 1))
6 setExclusiveOwnerThread(Thread.currentThread());
7 else
8 acquire(1);
9 }
10 //这里就是调用上面Sync类中尝试获取锁的方法
11 protected final boolean tryAcquire(int acquires) {
12 return nonfairTryAcquire(acquires);
13 }
14 }
FairSync类分析
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
//公平锁获取锁的实现和上面就不一样,这里没有通过CAS尝试获取锁,而是直接调用acquire方法
final void lock() {
acquire(1);
}
//尝试获取锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//这里就是和非公平锁不同的地方,这里调用了hasQueuedPredecessors,后面会分析这个方法
//剩下的和公平锁的实现就是一样的
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
进入AbstractQueuedSynchronizer #hasQueuedPredecessors()方法
1 public final boolean hasQueuedPredecessors() {
2 Node t = tail; // Read fields in reverse initialization order
3 Node h = head;
4 Node s;
5 //当前队列中不只有一个节点
6 //判断第二个节点是否为空,这个主要是为了防止第三个条件发生空指针异常,因为第一个条件虽然判断了队列中有第二个节点
7 //但是由于是在多线程环境下,随时都有可能第一个节点被干掉,第二个节点变成第一个节点,然后第二个就是null了,再之后就会发生空
8 //指针异常,这个返回的主要是为了判断当前线程是不是队列中的第二个节点,因为队列的第一个节点是虚拟节点,第二个节点才有获取锁的资格
9 //在公平锁的情况下是这样,但是非公平锁就不是这样了
10 return h != t &&
11 ((s = h.next) == null || s.thread != Thread.currentThread());
12 }
总结:公平锁的实现就是严格按照入队的先后顺序获取锁。
Lock接口
public interface Lock {
void lock();
//获取可中断锁
void lockInterruptibly() throws InterruptedException;
//尝试获取锁
boolean tryLock();
//try获取锁,设置锁最大等待时间,如果超时就取消获取
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
ReentrantLock构造方法
public ReentrantLock() {
sync = new NonfairSync();
}
//根据传入参数不同,初始化不同的类
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock和Synchronized对比
- 与
synchronized
相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候 - ReentrantLock 还提供了条件 Condition ,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock 更加适合。
- ReentrantLock 支持中断处理,且性能较
synchronized
会好些。 - ReentrantLock实现了公平锁和非公平锁的机制,而synchronized只有非公平一种模式
- synchronized不需要手动释放锁,但是ReentrantLock释放锁的时候,需要程序员自己释放
- ReentrantLock和synchronized都是通过内存屏障来保证有序性、可见性的,只不过ReentrantLock是volatile实现的,而synchronized不是使用volatile,但是volatile和synchronized在保证有序性和可见性上使用的内存屏障是一样的,所以也就是说两者基本相同
总结
本文介绍ReentrantLock源码结构以及实现原理,涉及到AQS的部分没有详细说明,主要是另一个篇文章已经介绍过,最后介绍了ReentrantLock和sychronized的区别。
参考:
【死磕 Java 并发】—– J.U.C 之重入锁:ReentrantLock