ReentrantLock 源码解析(源码基于JDK 1.7.80)
属性
构造方法
ReentrantLock 类只有两个构造方法,默认的构造方法为非公平锁。
ReentrantLock(boolean fair) 当 fair 为true 时是公平锁,当fair 为false 时是非公平锁。
从构造方法可以看出ReentrantLock 类的关键还是这个syns 对象,我们重点看一下这个类以及它的两个子类FairSync 和 NonfairSync。这两个子类分别实现了公平锁和非公平锁。
abstract static class Sync extends AbstractQueuedSynchronizer
上面这个就是Sync 类,它继承了AbstractQueuedSynchronizer(以下简称 AQS) 类。
我们来看看他是如何实现可重入性和不可重入性的
可重入非公平锁
获取锁
//加锁
final void lock() {
//CAS设置state状态,若原值是0,将其置为1
if (compareAndSetState(0, 1))
//将当前线程标记为已持有锁
setExclusiveOwnerThread(Thread.currentThread());
else
//若设置失败,调用AQS的acquire方法
acquire(1);
}
若线程拿到了锁就设置当前锁的拥有这为自己,若没有拿到锁 ,就调用acquire()方法,这个方法又调用了非公平锁重写的tryAcquire()方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();//当前线程
int c = getState();//当前状态值
if (c == 0) {
//若state为0,意味着没有线程获取到资源,CAS将state设置为1
//并将当前线程标记我获取到排他锁的线程,返回true
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//当前线程拿到了锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;//state累加1
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//设置state,此时state大于1,代表着一个线程多次获锁,state的值即是线程重入的次数
setState(nextc);
return true;
}
return false;
}
步骤总结
- 若当前锁没有被拿走,CAS操作修改锁的状态,并设置当前线程拥有锁。
- 若当前线程被自身拥有,将锁的线程加1
- 否则,获取锁失败
可重入公平锁
加锁
//获取锁
final void lock() {
acquire(1);
}
获取锁调用的是AQS的 acquire() 方法。
public final void acquire(int arg) {
//尝试获取锁,获取失败时加入等待队列。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire() 方法首先调用tryAcquire(arg) 方法,如果获取失败,就加入等待队列,加入成功就自己中断。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();//当前线程
int c = getState();//当前状态
if (c == 0) {//没有线程获得锁
//判断在时间顺序上有没有线程在等待,如果没有线程等待则,CAS设置state,
//并标记当前线程为持有排他锁的线程;反之,不能获取!这即是公平的处理方式。
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//当前线程获得了锁
else if (current == getExclusiveOwnerThread()) {
//state累加
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//设置state
setState(nextc);
//获取成功
return true;
}
//获取失败
return false;
}
}
总结步骤
- 判断当前锁有没有被其它线程拿走
- 若锁现在空闲就判断它前面有没有线程在等待若有线程在等待,就加入等待队列。
- 若锁被当前线程拥有使锁的状态加1
锁的释放
1)首先尝试释放锁,如果要求释放数等于锁状态数,那么将锁状态位清0,清除锁所有者,返回true;否则返回false;
(2)如果(1)返回的是true,说明锁完全释放。接下来将检查等待队列,并选择一个waitStatus处于等待状态的节点下的线程unpark(恢复),选择的依据是从尾节点开始,选取最靠近头节点的等待节点,同时清理队列中线程被取消的节点;
(3)如果(1)返回false,说明锁只是部分释放,当前线程仍旧持有该锁;
public final boolean release(int arg) {
8 if (tryRelease(arg)) {
9 Node h = head;
10 if (h != null && h.waitStatus != 0)
11 unparkSuccessor(h);
12 return true;
13 }
14 return false;
15 }
16
17
18 protected final boolean tryRelease(int releases) {
19 int c = getState() - releases; //重入锁加锁的次数-释放数量
20 if (Thread.currentThread() != getExclusiveOwnerThread()) //判断独占锁是否为当前线程所有
21 throw new IllegalMonitorStateException();
22 boolean free = false;
23 if (c == 0) { //加锁次数=释放数量
24 free = true;
25 setExclusiveOwnerThread(null); //清除锁拥有者标识
26 }
27 setState(c); //设置加锁状态
28 return free;
29 }
30
小结
通过上面的分析,我们大概了解了 ReentrantLock 的流程,ReentrantLock 内部有三个重要的静态内部类,Sync 、fairSync、NonfairSync。其中fairSync、NonfairSync继承自Sync 类,调用Sync的公用逻辑,然后再在各自内部完成自己特定的逻辑(公平或非公平)。Sync作为ReentrantLock中公用的同步组件,继承了AQS,要利用AQS复杂的顶层逻辑,线程排队,阻塞,唤醒等等。