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