在Java并发编程中,ReentrantLock是常用的线程同步工具,它提供了比synchronized更灵活的锁操作能力。其中lockInterruptibly方法允许线程在等待获取锁的过程中响应中断,解决了普通lock方法无法被中断的问题,适合需要支持取消等待锁的场景。
lockInterruptibly 方法的基本特性
ReentrantLock的lockInterruptibly方法是Lock接口定义的方法,它的核心特点是:当线程调用该方法尝试获取锁时,如果锁被其他线程持有,当前线程会进入阻塞状态等待锁,但在这个过程中如果其他线程对该线程发起了中断请求,等待中的线程会立即抛出InterruptedException并停止等待,而不是一直阻塞下去。
和普通的lock方法相比,两者的差异如下:
| 方法 | 等待锁时是否响应中断 | 获取不到锁时的行为 |
|---|---|---|
| lock() | 不响应 | 一直阻塞直到获取到锁 |
| lockInterruptibly() | 响应 | 阻塞等待,收到中断则抛出异常终止等待 |
基础使用示例
下面是一个简单的示例,演示两个线程竞争同一把锁,其中一个线程在等待锁的过程中被中断的场景:
import java.util.concurrent.locks.ReentrantLock;
public class LockInterruptiblyDemo {
// 创建可重入锁实例
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// 线程1先获取锁,持有锁一段时间
Thread thread1 = new Thread(() -> {
lock.lock();
try {
System.out.println("线程1获取到锁,开始执行任务");
// 模拟任务执行,持有锁3秒
Thread.sleep(3000);
System.out.println("线程1任务执行完成,释放锁");
} catch (InterruptedException e) {
System.out.println("线程1被中断");
} finally {
lock.unlock();
}
}, "thread-1");
// 线程2尝试用lockInterruptibly获取锁
Thread thread2 = new Thread(() -> {
try {
System.out.println("线程2尝试获取锁,等待中...");
// 支持中断的加锁方式
lock.lockInterruptibly();
try {
System.out.println("线程2成功获取到锁,执行任务");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("线程2在等待锁的过程中被中断,停止等待");
}
}, "thread-2");
// 启动线程
thread1.start();
// 确保线程1先获取到锁
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
// 等待1秒后中断线程2
try {
Thread.sleep(1000);
System.out.println("主线程中断线程2");
thread2.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行上述代码,输出结果大致如下:
线程1获取到锁,开始执行任务 线程2尝试获取锁,等待中... 主线程中断线程2 线程2在等待锁的过程中被中断,停止等待 线程1任务执行完成,释放锁
可以看到线程2在等待锁的过程中被中断后,立刻抛出了InterruptedException,没有继续等待线程1释放锁。
实际开发中的注意事项
- 调用lockInterruptibly方法后,一定要在try块外部捕获InterruptedException,或者在方法签名上声明抛出该异常,否则代码会编译报错。
- 获取锁成功后,一定要在finally块中释放锁,避免因为异常导致锁无法释放,引发死锁问题。
- 如果线程的中断状态已经被设置,再调用lockInterruptibly方法会立刻抛出InterruptedException,不会尝试获取锁。
- lockInterruptibly方法适合用在需要支持任务取消、超时等待等场景,比如线程池中的可取消任务、限时的资源获取逻辑等。
超时加锁结合中断的场景
ReentrantLock还提供了tryLock(long timeout, TimeUnit unit)方法,该方法同样支持中断,并且可以设置等待锁的超时时间,结合lockInterruptibly可以实现更灵活的加锁逻辑:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockInterruptDemo {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread taskThread = new Thread(() -> {
try {
// 尝试在2秒内获取锁,同时支持中断
if (lock.tryLock(2, TimeUnit.SECONDS)) {
try {
System.out.println("任务线程获取到锁,执行任务");
Thread.sleep(3000);
} finally {
lock.unlock();
}
} else {
System.out.println("等待锁超时,未获取到锁");
}
} catch (InterruptedException e) {
System.out.println("任务线程在等待锁时被中断");
}
});
lock.lock();
try {
System.out.println("主线程获取锁,启动任务线程");
taskThread.start();
// 等待1秒后中断任务线程
Thread.sleep(1000);
taskThread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
该示例中任务线程设置2秒的锁等待超时,同时在等待1秒时被中断,会立刻触发中断异常,不会继续等待剩余的1秒时间。
总结
ReentrantLock的lockInterruptibly方法为Java多线程加锁提供了中断响应能力,解决了普通加锁方式无法取消等待的问题。在实际开发中,需要根据场景选择合适的加锁方法:如果不需要支持中断,使用lock方法即可;如果需要允许线程在等待锁时可以被取消,优先使用lockInterruptibly或者带超时的tryLock方法。正确使用该特性可以提升多线程程序的健壮性,避免无意义的线程阻塞。
ReentrantLocklockInterruptiblyJava阻塞式加锁线程中断修改时间:2026-06-28 23:24:45