简介
一种可重入的独占锁。
- 从获取锁的机制上分为:公平锁、非公平锁;
- 锁在同一时间只能被一个线程持有;同一个线程可多次获取到锁;
- 加锁多少次,释放锁时也要释放同样的次数。
类图

说明:
FairSync:公平锁
NonFairSync:非公平锁
字段
private final Sync sync;
构造函数
- 默认创建非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
- 根据参数
fair确定创建的锁是否公平
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
内部类
abstract static class Sync extends AbstractQueuedSynchronizer
// 非公平锁
static final class NonfairSync extends Sync
// 公平锁
static final class FairSync extends Sync
主要 API
获取公平锁
lock源码如下:
public void lock() {
sync.lock();
}
接着调用FairSync类中的lock():
final void lock() {
acquire(1);
}
然后调用AQS中的acquire获取独占锁(抽象队列同步器 AQS)
AQS中完成了大部分的逻辑,其中tryAcquire需要子类去实现;
FairSync类中的tryAcquire源码如下:
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取同步状态(共享资源)
int c = getState();
// 为 0 表示资源未被占用,可以获取
if (c == 0) {
// 先判断在同步队列中是否有比当前更早的等待线程
// 没有的话当前线程就可以获取锁,并将同步状态置为 1
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 设置当前线程为持有锁的线程
setExclusiveOwnerThread(current);
return true;
}
}
// 不为 0 表示资源已被占用,判断下是否是当前线程占用的
else if (current == getExclusiveOwnerThread()) {
// 若是当前线程占用的则可以获取锁,并将同步状态加 1
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 同步状态不为 0 且资源不是当前线程占用的,则不能获取锁,返回 false
return false;
}
大概流程:
- 将获取公平锁的请求委托给
FairSync处理; FairSync又将请求转发给AQS处理;(抽象队列同步器 AQS)AQS中完成了大部分的逻辑,其中tryAcquire需要子类去实现;FairSync处理tryAcquire的逻辑;- 若资源(
state)未被占用,判断有没有比当前更早的线程,若有则获取失败,否则可以获取,并将state置为1; - 若资源(
state)已被占用,判断是不是当前线程占用的,不是则获取失败,否则可以获取,并将state加1;
- 若资源(
- 若以上获取失败,则由
AQS完成后续处理(当前线程加入同步队列等)。
获取非公平锁
lock源码如下:
public void lock() {
sync.lock();
}
接着调用NonfairSync类中的lock():
final void lock() {
// 若state为0则改为1,并设置当前线程为持有锁的线程
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 调用AQS处理请求
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取同步状态
int c = getState();
// 为 0 表示资源未被占用,可以获取
if (c == 0) {
// CAS修改状态为 1
if (compareAndSetState(0, acquires)) {
// 设置当前线程为持有锁的线程
setExclusiveOwnerThread(current);
return true;
}
}
// 不为 0 表示资源已被占用,判断下是否是当前线程占用的
else if (current == getExclusiveOwnerThread()) {
// 若是当前线程占用的则可以获取锁,并将同步状态加 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 同步状态不为 0 且资源不是当前线程占用的,则不能获取锁,返回 false
return false;
}
大概流程:
- 将获取非公平锁的请求委托给
NonfairSync处理,若资源未被占用直接去抢占资源;- 若抢占成功则将
state置为1,并设置当前线程为持有锁的线程; - 若抢占失败则将请求转发给
AQS处理;(抽象队列同步器 AQS)
- 若抢占成功则将
AQS中完成了大部分的逻辑,其中tryAcquire需要子类去实现;NonfairSync处理tryAcquire的逻辑;- 若资源未被占用,直接去抢占资源;
- 若资源已被占用,判断是不是当前线程占用的,不是则获取失败,否则可以获取,并将
state加1;
- 若以上获取资源失败,则由
AQS完成后续处理(当前线程加入同步队列等)。
释放锁
unlock源码如下:
public void unlock() {
sync.release(1);
}
将释放锁的请求转发给AQS处理;
AQS中完成了大部分的逻辑,其中tryRelease需要子类去实现;
ReentrantLock类中的tryRelease源码如下:
protected final boolean tryRelease(int releases) {
// 减去需要释放的资源数量
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 是否成功释放锁
boolean free = false;
// 只有当state为0时,才表示释放锁成功
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
大概流程:
释放锁的逻辑比较简单,每调用一次unlock,status就减1,当status为0时,表示锁释放成功。
示例
测试
备注
使用JDK版本为1.8