ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)
其实ReentrantLock并没有我们想的那么神秘,在我们之前的博客已经尝试使用过ReentrantLock,相信大家也有所感悟。ReentrantLock的底层是根据AbstractQueuedSynchronizer实现的,对于AbstractQueuedSynchronizer,可以参考我的这篇博客:Java并发编程之AQS
在学习ReentrantLock之前,我们有必要先了解一下什么是公平锁,什么是非公平锁。
在Java中,我们常用的synchronized实际上就是非公平锁。而对于我们将要讲解的ReentrantLock,它默认情况下是非公平锁,但是可以设置为公平锁。
ReentrantLock其实是一种可重入锁,若还不知道什么是可重入锁,可以参考我的这篇博客Java并发编程之Lock
我们先上ReentrantLock的定义
public class ReentrantLock implements Lock, java.io.Serializable
实现了Lock接口
ReentrantLock内部维护了三个内部类,Sync、NonfairSync、FairSync,其中Sync继承了AbstractQueuedSynchronizer抽象类,而NonfairSync、FairSync继承了Sync,下面进行详细分析。
先上定义
abstract static class Sync extends AbstractQueuedSynchronizer
这其实也是一个继承了AbstractQueuedSynchronizer的抽象静态类
abstract static class Sync extends AbstractQueuedSynchronizer {
// 序列号
private static final long serialVersionUID = -5179523762034025860L;
// 获取锁
abstract void lock();
// 非公平方式获取
final boolean nonfairTryAcquire(int acquires) {
// 当前线程
final Thread current = Thread.currentThread();
// 获取状态
int c = getState();
if (c == 0) { // 表示没有线程正在竞争该锁
if (compareAndSetState(0, acquires)) { // 比较并设置状态成功,状态0表示锁没有被占用
// 设置当前线程独占
setExclusiveOwnerThread(current);
return true; // 成功
}
}
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;
}
// 试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它
protected final boolean tryRelease(int releases) {
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;
}
// 判断资源是否被当前线程占有
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
// 新生一个条件
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
// 返回资源的占用线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 返回状态
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 资源是否被占用
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
// 自定义反序列化逻辑
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
NonfairSync采用了非公平方式来获得锁
static final class NonfairSync extends Sync {
// 版本号
private static final long serialVersionUID = 7316153563782823691L;
// 获得锁
final void lock() {
if (compareAndSetState(0, 1)) // 比较并设置状态成功,状态0表示锁没有被占用
// 把当前线程设置独占了锁
setExclusiveOwnerThread(Thread.currentThread());
else // 锁已经被占用,或者set失败
// 以独占模式获取对象,忽略中断
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSyn采用了公平方式来获得锁
static final 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.
*/
// 尝试公平获取锁
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取状态
int c = getState();
if (c == 0) { // 状态为0
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) { // 不存在已经等待更久的线程并且比较并且设置状态成功
// 设置当前线程独占
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 状态不为0,即资源已经被线程占据
// 下一个状态
int nextc = c + acquires;
if (nextc < 0) // 超过了int的表示范围
throw new Error("Maximum lock count exceeded");
// 设置状态
setState(nextc);
return true;
}
return false;
}
}
当资源空闲时,它总是会先判断sync队列是否有等待时间更长的线程,如果存在,则将该线程加入到等待队列的尾部,实现了公平获取原则。
private final Sync sync;
实际上这就是一个同步队列,对ReentrantLock的操作大多转化为对sync的操作
public ReentrantLock() {
sync = new NonfairSync();
}
默认采用非公平策略
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
通过传递参数确定采用公平策略或者是非公平策略
ReentrantLock的操作大多转化为对Sync的操作,实际上就是对AQS的操作。由于我们之前详细讲解过AQS,这里不再赘述。
对于ReentrantLock的实现来说,其实非公平锁的性能远远优于公平锁,为什么呢?原因在于,非公平锁对锁的竞争是抢占式的(队列中线程除外),线程在进入等待队列前可以进行两次尝试,这大大增加了获取锁的机会。这体现在下面两个方面。
ReentrantLock与synchronized的功能与内存语义大致相同
参考:【JUC】JDK1.8源码分析之ReentrantLock(三)
Java多线程(九)之ReentrantLock与Condition
从源码角度彻底理解ReentrantLock(重入锁)