ReentrantLock源码解读
大约 4 分钟
如果我们对AQS有过理解过,再来看RentrantLock的代码就相当容易了。 我们都知道ReentrantLock有公平和非公平之分,而AQS是没有这个公平或非公平的概念的。 下面我们通过源码来看看ReentrantLock的源码并分析其公平和非公平的实现。
ReentrantLock的代码很简单,其核心代码就是继承自AQS的三个内部类。
其类继承结构如下:
下面先贴代码然后进行分析总结,代码中添加了注释
Sync内部类
删除一些次要代码后:
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) {
//如果无锁状态,直接进行cas操作
if (compareAndSetState(0, acquires)) {
//成功后标记当前线程为锁的拥有者
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;
}
//实现AQS中的方法,实现释放锁的逻辑
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;
}
}
NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
//实现父类Sync的方法
final void lock() {
//不管是有被占用,先进行一次cas操作
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//实现AQS类中的方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
//这一点和NonfairSync有区别
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//如果无锁状态,再判断队列是否有线程在等待锁,如果没有才进行cas操作
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;
}
}
补充代码
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
总结
- ReentrantLock默认构造方法是非公平的
- ReentrantLock是独占锁,它只使用了AQS的独占锁和条件队列功能,并没有使用共享锁。
- 非公平锁比公平锁多了两次执行cas操作的可能。
- 想要获得锁的时候,不管队列情况如何先尝试进行cas操作。(公平锁没有这个cas操作)
- 如果第一次cas失败,在执行tryAcquire的时候,如果当前无锁,又会执行一次cas操作。 (公平锁执行这次cas的操作的时候会先判断队列情况)
相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。
sync和reentrantlock的区别
- 两者都是可重入的(sync的重入次数保存在哪里的呢?)
- sync是基于jvm实现的,是隐式锁,而ReentrantLock是基于java api实现的,是显式锁。
- ReentrantLock 比 synchronized 更加灵活
- 可以试探性获得锁(tryLock)
- 等待可中断;(acquireInterruptibly)
- 可实现公平锁,sync是非公平的
- 可实现选择性通知,也就是可以创建多个条件队列
- 性能上已经不是一个选择因素了。sync做了各种优化(偏向锁、自旋锁、锁粗化、锁消除等等)
系统推荐
- MAT工具
- Oh My ZSH
- MySQL杂项
- 微博关注关系如何实现
- 搭建基于docker的elk平台来分析java日志
- Docker笔记
- 行转列不再复杂:SQL高手都在用的技巧揭秘
- AQS源码解读
- MySQL三大日志
- 批量替换文件名中的指定字符串
- raft协议
- WebSocket SpringBoot Demo
- 随机毒鸡汤:昨天越来越多,明天越来越少。