ReentrantLock的lockInterruptibly有什么用
文章目录
- 1. 标准 `lock()` 方法的问题
- 2. `lockInterruptibly()` 的解决方案
- 3. `lock()` vs `lockInterruptibly()` 对比
- 4. 使用场景示例:响应取消操作
- 总结
ReentrantLock.lockInterruptibly()
是 ReentrantLock
提供的一个非常重要的获取锁的方法,它的核心作用是:在等待获取锁的过程中,允许线程响应中断。
这与标准的 lock()
方法形成了鲜明对比,理解它们的区别是掌握 ReentrantLock
的关键。
1. 标准 lock()
方法的问题
首先,我们来看一下标准的 lock()
方法:
lock.lock();
try {// ... 临界区代码 ...
} finally {lock.unlock();
}
lock()
方法会一直尝试获取锁。如果锁被其他线程持有,当前线程就会进入阻塞状态,并且对中断信号(Thread.interrupt()
)完全无动于衷。它会一直傻等下去,直到成功获取到锁为止。
这在某些场景下会成为一个问题。比如:
- 一个任务正在等待一个永远不会被释放的锁(例如,持有锁的线程崩溃了),你希望能够取消这个等待的任务,但它对中断没反应。
- 发生死锁时,所有相关线程都在使用
lock()
相互等待,它们都无法被中断,导致程序永久卡死。
2. lockInterruptibly()
的解决方案
lockInterruptibly()
方法就是为了解决上述问题而设计的。它的行为是:
- 尝试获取锁:和
lock()
一样,如果锁是可用的,它会立即获取锁并返回。 - 进入可中断的等待:如果锁不可用,当前线程会进入阻塞等待状态。但与
lock()
不同的是,这个等待状态是可以被中断的。 - 响应中断:如果在等待期间,有其他线程调用了
waitingThread.interrupt()
来中断这个正在等待的线程,那么:lockInterruptibly()
会立即停止等待。- 它不会继续尝试获取锁,而是会抛出
InterruptedException
异常。 - 同时,线程的中断状态会被清除。
3. lock()
vs lockInterruptibly()
对比
特性 | lock() | lockInterruptibly() |
---|---|---|
中断响应 | 不响应中断。会一直等待直到获取锁。 | 响应中断。在等待期间如果被中断,会立即停止并抛出异常。 |
抛出异常 | 不会因为中断而抛出异常。 | 会抛出 InterruptedException 。 |
使用场景 | 适用于不关心线程中断,必须获取到锁才能继续的场景。 | 适用于需要可取消或可超时的锁等待操作,以及避免死锁的场景。 |
4. 使用场景示例:响应取消操作
想象一个场景,一个长时间运行的任务需要获取一个锁,但我们希望用户可以随时取消这个任务。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockInterruptiblyExample {private static final Lock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {// 线程 T1 先获取锁并且长时间持有Thread t1 = new Thread(() -> {lock.lock();try {System.out.println("T1: 获取到锁,并开始长时间工作...");Thread.sleep(10000); // 模拟长时间持有锁} catch (InterruptedException e) {// ...} finally {System.out.println("T1: 工作完成,释放锁。");lock.unlock();}}, "T1");// 线程 T2 尝试获取锁,但使用 lockInterruptibly()Thread t2 = new Thread(() -> {System.out.println("T2: 尝试获取锁...");try {// 使用 lockInterruptibly(),使得等待过程可以被中断lock.lockInterruptibly();try {System.out.println("T2: 终于获取到锁了!");} finally {lock.unlock();}} catch (InterruptedException e) {System.out.println("T2: 在等待锁的过程中被中断了,不再等待!");}}, "T2");t1.start();// 确保 T1 先拿到锁Thread.sleep(100);t2.start();// 让 T2 等待2秒Thread.sleep(2000);// 主线程决定不等了,取消 T2 的任务System.out.println("Main: 决定中断 T2 的等待。");t2.interrupt(); // 中断 T2}
}
输出结果:
T1: 获取到锁,并开始长时间工作...
T2: 尝试获取锁...
Main: 决定中断 T2 的等待。
T2: 在等待锁的过程中被中断了,不再等待!
T1: 工作完成,释放锁。
分析:
T1
获取了锁并持有不放。T2
调用lockInterruptibly()
开始等待锁,进入阻塞。- 主线程在2秒后调用
t2.interrupt()
。 - 正在等待的
T2
立刻响应了这个中断,lockInterruptibly()
抛出InterruptedException
,T2
进入catch
块并结束,它放弃了对锁的等待。
如果 T2
使用的是 lock()
,那么它会一直等到 T1
在10秒后释放锁,期间对 interrupt()
调用完全不理会。
总结
lockInterruptibly()
提供了一种可取消的、对中断友好的锁获取机制。当你需要编写健壮的、能及时响应取消或关闭信号的并发代码时,它是一个非常重要的工具,尤其是在处理可能长时间阻塞或潜在死锁的场景中。
其实还是lock方法的变种,只不过一个是死等,一个是可以响应中断的等,A死等,B等了半天,完了之后C叫他喂喂喂,别等了,赶紧撤吧,B就哦哦哦,于是B提桶跑路了