ReentrantLock可重入锁
摘要:ReentrantLock是Java提供的可重入锁实现,相比synchronized具有更丰富的功能,包括支持公平/非公平锁、可中断获取、超时机制和多条件变量等。其核心方法包含lock()、tryLock()、unlock()等操作,通过Condition可实现精细的线程等待/唤醒控制。内部采用Sync、NonfairSync和FairSync三个类实现不同的锁策略。与synchronize
1.基本概念
1.1.什么事可重入锁?
ReentrantLock是Java.util.concurrent.locks包下的一个类,它实现Lock 接口,提供比 synchronized 更灵活、强大的锁机制。"可重入"意味着同一个线程可以多次获取同一把锁,而不会被阻塞。
1.2.为什么需要ReentranLock?
相比于synchronized而言,ReentranLock提供了以下优势:
(1)同时支持公平锁和非公平锁;
(2)获取锁的过程,可以中断;
(3)获取锁的过程中,可以设置超时;
(4)支持多个条件变量(Condition);
(5)支持非阻塞地方尝试获取锁;
2.核心方法
2.1.锁的操作方法
2.1.1构造方法
// 创建一个非公平锁
ReentrantLock rtl= new ReentrantLock()
// 根据公平策略创建锁
ReentrantLock rtl= new ReentrantLock(boolean fair)
2.1.2.其他方法
| 方法 | 描述 | 是否可中断 | 是否阻塞 | 是否带超时 |
|---|---|---|---|---|
void lock() |
获取锁,若锁被占用则阻塞等待 | ❌ 不可中断 | ✅ 阻塞 | ❌ 无超时 |
void lockInterruptibly() |
获取锁,阻塞时可响应中断 | ✅ 可中断 | ✅ 阻塞 | ❌ 无超时 |
boolean tryLock() |
尝试获取锁,立即返回成功或失败 | ❌ 不可中断 | ❌ 非阻塞 | ❌ 无超时 |
boolean tryLock(long timeout, TimeUnit unit) |
尝试获取锁,带超时等待 | ✅ 可中断 | ✅ 超时前阻塞 | ✅ 可设超时 |
void unlock() |
释放锁 | - | - | - |
int getHoldCount() |
返回当前线程重入锁的次数 | - | - | - |
boolean isHeldByCurrentThread() |
检查当前线程是否持有锁 | - | - | - |
boolean isLocked() |
检查锁是否被任意线程持有 | - | - | - |
boolean isFair() |
判断是否为公平锁 | - | - | - |
getQueuedThreads() |
返回等待获取锁的线程集合 | - | - | - |
int getQueueLength() |
返回等待获取锁的线程数 | - | - | - |
boolean hasWaiters(Condition condition) |
检查是否有线程在等待指定条件 | - | - | - |
2.2条件变量方法
根据创建的ReentrantLock对象,创建条件变量
ReentrantLock lock=new ReentrantLock();
Condition condition = lock.newCondition();
补充:Condition的主要用法
主要方法
| 方法 | 作用 | 对应Object方法 |
|---|---|---|
await() |
释放锁并等待 | wait() |
await(long time, TimeUnit unit) |
限时等待 | wait(long timeout) |
awaitNanos(long nanosTimeout) |
纳秒级限时等待 | - |
awaitUninterruptibly() |
不可中断的等待 | - |
signal() |
唤醒一个等待线程 | notify() |
signalAll() |
唤醒所有等待线程 | notifyAll() |
3.使用案例
3.1锁的基础使用
package Lock;
import java.util.concurrent.locks.ReentrantLock;
//ReentrantLock
public class Demo01 {
public static void main(String[] args) {
Counter counter = new Counter();
Counter counter2 = new Counter();
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
counter.add();
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
counter2.sub();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main=>"+counter.countValue);
}
}
class Counter {
public static int countValue = 0;
//锁对象
public static final ReentrantLock lock = new ReentrantLock();
public void add() {
lock.lock();//加锁
try {
for (int i = 0; i < 10000; i++) {
countValue++;
}
} finally {
lock.unlock();//释放锁
}
}
public void sub() {
lock.lock();
try {
for (int i = 0; i < 10000; i++) {
countValue--;
}
} finally {
lock.unlock();
}
}
}
运行结果:

要使用ReentrantLock就要创建锁对象,此段代码两个线程共用了一个锁对象
package Lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
public static void main(String[] args) {
TicketPool ticketPool=new TicketPool(20);
Thread t1=new Thread(ticketPool,"窗口1");
Thread t2=new Thread(ticketPool,"窗口2");
Thread t3=new Thread(ticketPool,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class TicketPool implements Runnable{
//当前剩余门票数量
private int ticketNumber ;
//锁对象
private final ReentrantLock lock = new ReentrantLock();
// 条件对象
private final Condition condition = lock.newCondition();
public TicketPool(int ticketNumber) {
this.ticketNumber = ticketNumber;
}
@Override
public void run() {
//售票逻辑
System.out.println(Thread.currentThread().getName()+"准备开始售票");
lock.lock();
try {
while(true){
if (ticketNumber <=0){
System.out.println(Thread.currentThread().getName()+"票已售完");
return;
}else {
System.out.println(Thread.currentThread().getName()+"正在出售第"+ticketNumber+"张票,还剩下"+--ticketNumber+"张票");
try {
condition.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}finally {
lock.unlock();
}
}
}
4.ReentrantLock内部结构
ReentrantLock实现了 Lock接,Lock接中定义了lock()、unlock() ,tryLock()等相关操作。
ReentrantLock总共有三个内部类:Sync,NofairSync,FairSync
Nonfairsync 类继承了 sync类,表示采用非公平策略获取锁:每一次都尝试获取锁,不会按照公平等待的原则进行等待,不会让等待时间最久的线程获得锁。
Fairsync 类也继承了 sync 类,表示采用公平策略获取锁:当资源空闲时,它总是会先判断同步队列中,是否有等待时间更长的线程,如果存在,则将当前线程加入到等待队列的尾部,实现了公平获取原则。
ReentrantLock 构造函数:默认是采用的非公平策略获取锁。
5.Synchronized和ReentrantLock的区别
| 特性 | synchronized (内置锁) |
ReentrantLock (显式锁) |
|---|---|---|
| 实现方式 | JVM 关键字,自动管理 | JDK 类,需要手动编码实现 |
| 锁获取 | 隐式获取和释放 | 必须显式调用 lock() 和 unlock() |
| 可中断性 | 不可中断 | 支持 lockInterruptibly() 可中断等待 |
| 公平性 | 非公平锁 | 可选择公平/非公平(构造方法参数) |
| 条件队列 | 单一等待队列 | 可创建多个 Condition 实现精细控制 |
| 性能 | JDK6 后优化,性能相当 | 高竞争时表现更好 |
| 锁绑定条件 | 只能绑定一个监视器 | 可绑定多个 Condition |
| 锁状态查询 | 无法查询 | 提供 tryLock()、isLocked() 等方法 |
| 超时机制 | 不支持 | 支持 tryLock(timeout) 超时获取 |
| 代码灵活性 | 语法简单,但控制粒度粗 | 控制精细,但需要手动释放 |
| 异常处理 | 自动释放锁 | 必须在 finally 中释放锁 |
| 适用场景 | 简单同步场景 | 复杂同步需求(如公平性、可中断、多条件等) |
更多推荐



所有评论(0)