ReentrantLock详解
ReentrantLock是基于AQS框架的一种实现,实现了Lock接口,有一个Sync的内部类,Sync是一个由abstract修饰继承自AbstractQueuedSynchronizer(AQS)的类。
ReentrantLock还有两个内部类NonfairSync和FairSync,前者是非公平锁同步对象,后者是公平锁同步对象,二者都继承自Sync。
零基础可以先这样理解:
ReentrantLock是一个可以重复进入的互斥锁。
“互斥”表示同一时刻只允许一个线程进入临界区;“可重入”表示同一个线程已经拿到锁以后,可以再次拿同一把锁,不会把自己堵住。可重入次数记录在 AQS 的 state 里。
工作原理图
flowchart TD
A["线程调用 lock"] --> B{"state == 0?"}
B -- "是" --> C{"CAS 把 state 从 0 改为 1"}
C -- "成功" --> D["设置 owner 为当前线程"]
C -- "失败" --> E["进入 AQS 队列等待"]
B -- "否" --> F{"owner 是当前线程?"}
F -- "是" --> G["state + 1\n表示重入一次"]
F -- "否" --> E
E --> H["线程 park 挂起\n等待 unlock 唤醒"]
I["当前线程 unlock"] --> J["state - 1"]
J --> K{"state == 0?"}
K -- "否" --> L["仍然持有锁\n不唤醒其他线程"]
K -- "是" --> M["清空 owner\n唤醒队列后继线程"]所以,lock() 不是简单地“加一个标记”。它还要处理抢锁失败后的排队、同一线程再次进入、释放时是否真正释放完成等问题。
使用 Demo:保护共享变量
import java.util.concurrent.locks.ReentrantLock;
public class CounterService {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}unlock() 必须放在 finally 里。否则业务代码抛异常后锁没有释放,后续线程会一直等待。
公平锁和非公平锁的区别
flowchart TD
A["线程请求锁"] --> B{"公平锁?"}
B -- "是" --> C["先检查 AQS 队列\n前面有人就排队"]
B -- "否" --> D["先尝试直接 CAS 抢锁\n失败再排队"]
C --> E["更接近先来先服务\n吞吐量可能低一些"]
D --> F["吞吐量通常更高\n但等待线程可能被插队"]默认构造方法创建的是非公平锁,因为多数业务更看重吞吐量。只有当你明确需要接近“先到先得”的等待顺序时,才考虑 new ReentrantLock(true)。
构造函数
/**
* 创建一个ReentrantLock实例默认创建非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 可以指定公平锁和非公平锁的构造器
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}Lock接口
Lock接口定义了一些锁相关的抽象接口,如下:
public interface Lock {
// 获取锁
void lock();
// 可中断的获取锁
void lockInterruptibly() throws InterruptedException;
// 尝试获取锁-只有在调用时锁是空闲的情况下才获取锁
boolean tryLock();
// 给定时间内尝试获取锁-如果锁在给定的等待时间内是空闲的,并且当前线程没有被中断,则获取锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁。
void unlock();
// 返回绑定到此Lock实例的新Condition实例
Condition newCondition();
}Sync解说
Sync是继承自AbstractQueuedSynchronizer,ReentrantLock的静态内部类,是同步控制的基础。使用AQS的state代表持有锁的次数。
nonfairTryAcquire方法
Sync内部类中包含非公平锁实现(nonfairTryAcquire),tryAcquire是在Sync的子类中实现的。
非公平锁获取锁方法
/**
* 非公平锁的尝试获取锁
*/
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取同步状态state值
int c = getState();
// state为0,表示当前线程没有持有锁,尝试获取锁
if (c == 0) {
// 通过CAS设置state值为acquires
if (compareAndSetState(0, acquires)) {
// 设置独占线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
// state不为0,表示已有线程持有锁,判断独占线程是否为当前线程
else if (current == getExclusiveOwnerThread()) {
// 计算新的state值判断是否溢出
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//未溢出讲新值设置给state
setState(nextc);
return true;
}
return false;
}NonfairSync和FairSync
NonfairSync和FairSync都继承自Sync,NonfairSync是非公平锁实现,FairSync是公平锁实现。
NonfairSync
源码如下:
/**
* 非公平锁的同步对象
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 执行加锁
* 通过CAS更新同步状态
* 如果CAS成功则设置独占线程为当前线程,否则执行acquire方法
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}FairSync
源码如下:
/**
* 公平锁同步对象
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* 公平的tryAcquire.
* 除非递归调用或没有等待程序,否则不要授予访问权限
*/
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取同步状态值
int c = getState();
// 如果同步状态为0
if (c == 0) {
// 查询是否存在线程在等待获取资源,如果没有则通过CAS设置同步状态为acquires,成功则设置独占线程为当前线程;
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//同步状态不为0判断当前线程是否为独占线程
else if (current == getExclusiveOwnerThread()) {
// 计算同步状态新值
int nextc = c + acquires;
// int类型值溢出抛异常
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 更新新值
setState(nextc);
return true;
}
return false;
}
}acquire详见AQS独占模式-获取资源
