JUC 的 Lock 接口 + ReentrantLock 完整用法
·
一、什么是Lock
java.util.concurrent.locks.Lock 是 Java 提供的比 synchronized 更灵活、功能更强的锁接口。它的核心作用:控制多线程同时访问共享资源,保证线程安全。
最常用的实现类:ReentrantLock(可重入锁)
二、Lock 最标准、最安全的写法
Lock lock = new ReentrantLock();
lock.lock(); // 加锁
try {
// 临界区(安全操作共享资源)
} finally {
lock.unlock(); // 必须在 finally 里解锁!!
}
为什么必须放 finally?
因为如果代码中间抛异常,锁也能保证被释放,不会造成死锁。
三、ReentrantLock 5 大核心用法
我直接给你完整可运行代码,复制就能跑。
1. 基础用法:lock () /unlock ()(最常用)
作用:独占锁,拿不到就一直等待。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo1 {
private static int count = 0;
private static final Lock lock = new ReentrantLock();
public static void increment() {
// 加锁
lock.lock();
try {
count++;
} finally {
// 解锁(必须放finally)
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终结果:" + count); // 一定是 2000
}
}
2. 可尝试获取锁:tryLock ()
作用:尝试拿锁,拿不到就不等待,直接返回 false。避免死锁神器!
public void tryLockTest() {
if (lock.tryLock()) {
try {
System.out.println("成功获取锁");
} finally {
lock.unlock();
}
} else {
System.out.println("没拿到锁,去做别的事");
}
}
3. 定时获取锁:tryLock (long time, TimeUnit unit)
作用:等一段时间,拿不到就放弃。
try {
// 等 1 秒
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
System.out.println("1秒内拿到锁");
} finally {
lock.unlock();
}
} else {
System.out.println("1秒都没拿到,算了");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
4. 可中断锁:lockInterruptibly ()
synchronized 不能中断,会一直死等。ReentrantLock 可以被别的线程打断等待。
- 先看 synchronized 不能被中断 的效果
- 再看 lockInterruptibly () 可以被中断 的效果
public class SynchronizedInterruptDemo {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
// 线程1:先占有锁,一直不释放
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println("t1 拿到锁,开始睡觉");
// 睡很久,不释放锁
try { Thread.sleep(1000000); } catch (InterruptedException e) {}
}
});
// 线程2:去抢锁,会一直阻塞
Thread t2 = new Thread(() -> {
System.out.println("t2 尝试获取 synchronized 锁");
synchronized (lock) {
System.out.println("t2 拿到锁了");
}
System.out.println("t2 正常结束");
});
t1.start();
Thread.sleep(100); // 确保t1先拿到锁
t2.start();
Thread.sleep(1000);
System.out.println("主线程尝试中断 t2");
t2.interrupt(); // 试图中断阻塞中的t2
}
}
运行结果你会看到:
- t2 一直卡在 synchronized 那里
- 主线程调用了
t2.interrupt() - t2 完全没反应,不会抛出异常,也不会退出这就是:synchronized 在等待锁时,不响应中断
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockInterruptiblyDemo {
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
// 线程1:先拿到锁,一直占着
Thread t1 = new Thread(() -> {
lock.lock();
try {
System.out.println("t1 拿到锁,开始睡觉");
Thread.sleep(1000000); // 一直占锁
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
// 线程2:用 lockInterruptibly() 抢锁
Thread t2 = new Thread(() -> {
System.out.println("t2 尝试获取锁(可中断)");
try {
// 关键方法:可中断的获取锁
lock.lockInterruptibly();
try {
System.out.println("t2 成功拿到锁");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
// 被中断后会进入这里!
System.out.println("t2 在等锁的过程中被中断了!");
e.printStackTrace();
}
});
t1.start();
Thread.sleep(100);
t2.start();
Thread.sleep(1000);
System.out.println("主线程调用 t2.interrupt()");
t2.interrupt(); // 中断正在等锁的 t2
}
}
运行结果(非常关键)
t1 拿到锁,开始睡觉
t2 尝试获取锁(可中断)
主线程调用 t2.interrupt()
t2 在等锁的过程中被中断了!
java.lang.InterruptedException
at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1264)
你学到了什么?
-
lock.lock():等锁时不响应中断,跟 synchronized 一样死等 -
lock.lockInterruptibly():等锁时响应中断一旦别的线程调用thread.interrupt(),它会立刻抛出 InterruptedException,退出等待
5. 公平锁 / 非公平锁
- 非公平锁(默认):谁抢到是谁的,速度快
- 公平锁:谁等得久谁先拿,不会线程饥饿
Lock fairLock = new ReentrantLock(true); // 公平锁
Lock noFairLock = new ReentrantLock(); // 非公平锁(默认)
四、ReentrantLock 最牛功能:Condition 线程精准通信
synchronized 只能用 wait/notify 随机唤醒。ReentrantLock 可以指定唤醒某个线程!
案例:生产者 - 消费者(精准唤醒)
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
private final Lock lock=new ReentrantLock();
private final Condition produceCond=lock.newCondition();
private final Condition consumerCond=lock.newCondition();
private boolean hasGoods=false;
public void produce() throws InterruptedException {
lock.lock();
try{
while(hasGoods){
produceCond.await();
}
System.out.println(Thread.currentThread().getName()+"生产商品");
hasGoods=true;
consumerCond.signal();
} finally {
lock.unlock();
}
}
public void consume() throws InterruptedException {
lock.lock();
try{
while(hasGoods==false){
consumerCond.await();
}
System.out.println(Thread.currentThread().getName()+"消费产品");
hasGoods=false;
produceCond.signal();
}finally {
lock.unlock();
}
}
}
package bagu_code;
public class Main {
public static void main(String[] args) throws InterruptedException {
//User user=User.Builder().name("张三").age(18).build();
ConditionDemo demo=new ConditionDemo();
Thread p1=new Thread(()->{
try {
while(true){
demo.produce();
Thread.sleep(100);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"p1");
Thread c1=new Thread(()->{
try {
while(true){
demo.consume();
Thread.sleep(100);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"c1");
Thread c2=new Thread(()->{
try {
while (true){
demo.consume();
Thread.sleep(100);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"c2");
p1.start();
c1.start();
c2.start();
p1.join();
c1.join();
c2.join();
}
}
可以运行看看效果。
五、synchronized VS ReentrantLock
表格
| 对比项 | synchronized | ReentrantLock |
|---|---|---|
| 释放 | 自动释放 | 必须手动 unlock |
| 可中断 | ❌ 不支持 | ✅ 支持 |
| 公平锁 | ❌ 不支持 | ✅ 支持 |
| 等待队列 | 一个 | 多个(Condition) |
| 尝试加锁 | ❌ | ✅ tryLock() |
| 性能 | 低并发够用 | 高并发更强 |
| 使用难度 | 简单 | 稍复杂 |
六.ReentrantLock的使用场景
你只要记住这 4 个场景:
- 需要尝试获取锁(不想死等)
- 需要定时获取锁
- 需要公平锁
- 需要多个等待队列、精准唤醒线程
七、总结
- ReentrantLock = 可重入、功能强大的锁
- 必须 lock () + try-finally + unlock ()
- 核心方法:
lock()一直等tryLock()尝试拿tryLock(1s)定时拿lockInterruptibly()可中断
- Condition 可以精准唤醒线程
- 高并发、需要灵活控制 → 用 ReentrantLock
八、一些使用场景
案例 1:tryLock (超时时间) + 可中断(真实业务推荐)
这是接口超时控制、防止线程堆积最常用写法:
- 最多等 3 秒
- 3 秒内拿不到 → 放弃
- 等待过程中可被中断
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockTimeoutExample {
private static final Lock lock = new ReentrantLock();
public static void businessMethod() {
boolean acquired = false;
try {
// 最多等 3 秒,且支持中断
acquired = lock.tryLock(3, TimeUnit.SECONDS);
if (!acquired) {
System.out.println(Thread.currentThread().getName() +
":3秒内未获取到锁,接口超时,直接返回失败");
return;
}
// 拿到锁,执行业务
System.out.println(Thread.currentThread().getName() + ":获取锁成功,开始执行业务");
Thread.sleep(2000); // 模拟业务执行
} catch (InterruptedException e) {
// 等待过程中被中断
System.out.println(Thread.currentThread().getName() + ":等待锁时被中断");
Thread.currentThread().interrupt(); // 恢复中断标志(规范写法)
} finally {
if (acquired) {
lock.unlock();
System.out.println(Thread.currentThread().getName() + ":释放锁");
}
}
}
public static void main(String[] args) throws InterruptedException {
// 线程1先拿锁5秒
new Thread(() -> {
lock.lock();
try {
System.out.println("线程1拿到锁,占用5秒");
Thread.sleep(5000);
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}, "thread-1").start();
Thread.sleep(100);
// 线程2尝试抢锁,只等3秒
new Thread(TryLockTimeoutExample::businessMethod, "thread-2").start();
}
}
执行结果
线程1拿到锁,占用5秒
thread-2:3秒内未获取到锁,接口超时,直接返回失败
案例 2:综合实战(接口限流 + 超时 + 防死锁)
模拟一个订单接口,同一用户只能一个线程进入,超时直接返回。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class OrderServiceDemo {
// 用户锁
private final Lock userLock = new ReentrantLock();
public void createOrder(Long userId) {
System.out.println("用户" + userId + "开始请求创建订单");
boolean lockSuccess = false;
try {
// 最多等 2 秒
lockSuccess = userLock.tryLock(2, TimeUnit.SECONDS);
if (!lockSuccess) {
System.err.println("用户" + userId + "请求超时:系统繁忙,请稍后重试");
return;
}
// 模拟下单逻辑
System.out.println("用户" + userId + "进入下单逻辑");
Thread.sleep(3000); // 下单耗时3秒
System.out.println("用户" + userId + "订单创建成功");
} catch (InterruptedException e) {
System.err.println("用户" + userId + "请求被中断");
Thread.currentThread().interrupt();
} finally {
if (lockSuccess) {
userLock.unlock();
}
}
}
public static void main(String[] args) {
OrderServiceDemo service = new OrderServiceDemo();
// 模拟两个并发请求
new Thread(() -> service.createOrder(1001L)).start();
new Thread(() -> service.createOrder(1001L)).start();
}
}
结果
用户1001开始请求创建订单
用户1001开始请求创建订单
用户1001进入下单逻辑
用户1001请求超时:系统繁忙,请稍后重试
用户1001订单创建成功
更多推荐




所有评论(0)