一、介绍
ReentrantLock
是JUC包下的一个类,他是可重入锁,同一个线程可以对资源重复加锁。ReentrantLock
而且支持公平和非公平的模式,公平模式下按等待时间来排队获取。也就是一个FIFO队列
(synchronized
是隐式支持重入的)
二、源码分析
ReentrantLock
内部是通过组合的方式,实现自定义的同步同步器
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 非公平锁
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
/**
* 公平锁
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
// 省略部分代码
}
}
/**
* 构造器中构造自定义同步器
* 默认是非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
通过源码可以知道ReentrantLock 默认是非公平模式。
2.1 非公平锁-加锁
final void lock() {
// 尝试加锁
// AQS内部有一个 state来表示资源的占用情况
// 0表示未被占用
if (compareAndSetState(0, 1))
// 设置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 内部进入阻塞 (内部也有可重入的逻辑)
acquire(1);
}
核心方法,非公平的方式获取资源
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
* 以非公平的方式 获取资源
*/
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取当前资源状态 0 初始 1资源被占用
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;
}
- 主要流程就是
- 判断资源状态
- CAS更新
- 锁重入
2.2 释放锁
/**
* 进行锁的释放 当 state资源为0
* 表示锁已经被释放了
* @param releases
* @return
*/
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;
}
2.3 公平锁加锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 即加入了同步队列中当前节点是否有前驱节点的判断,如果该 方法返回true,
// 则表示有线程比当前线程更早地请求获取锁
// 其实就是判断是否 有在等待的(除非自己),如果在等待,不好意思没机会,你慢慢在
// 后续加入队列后,慢慢等
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;
}
- 公平锁的最大区别是多了一个
hasQueuedPredecessors
方法,判断是否在等待队列中
三、小结
ReentrantLock
默认是非公平锁,可以抢占获取锁- 公平锁的模式 按请求顺序阻塞获取锁 FIFO获取
四、参考
《Java并发编程的艺术》