在写多ref="/tag/413/" style="color:#C468A7;font-weight:bold;">线程程序时,很多人用过 synchronized,但可能对 ReentrantLock 的细节了解不多。特别是它的可中断特性,其实是个很实用的功能,能避免线程陷入无休止的等待。
什么是 ReentrantLock 的可中断特性?
简单说,就是当一个线程正在等待获取锁的时候,如果它被其他线程调用了 interrupt() 方法,它可以立即停止等待,转而去处理中断信号。这跟 synchronized 不一样,synchronized 是无法响应中断的,一旦开始等,就得等到拿到锁为止,哪怕你已经不想等了。
举个生活中的例子:你在银行排队办业务,前面人特别多。如果是 synchronized 模式,你就得一直排下去,哪怕突然接到电话说家里有急事,你也只能硬着头皮等。但如果是 ReentrantLock 的 lockInterruptibly(),你可以选择“我不等了”,直接离开队伍,去处理更紧急的事。
代码怎么用?
关键在于使用 lockInterruptibly() 方法,而不是普通的 lock():
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
try {
lock.lockInterruptibly(); // 可中断地获取锁
try {
// 执行临界区操作
System.out.println("线程 " + Thread.currentThread().getName() + " 正在工作...");
Thread.sleep(5000); // 模拟耗时操作
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("线程 " + Thread.currentThread().getName() + " 被中断,不再等待锁");
}
}, "t1");
Thread t2 = new Thread(() -> {
lock.lock();
try {
System.out.println("线程 t2 拿到了锁,开始工作...");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t2");
t1.start();
t2.start();
// 让 t1 等一会后强制中断
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt(); // 中断 t1
运行这段代码,你会发现 t1 并没有傻等 10 秒,而是在被中断后立刻退出了等待状态,打印出“被中断”的提示。这就是可中断特性的实际效果。
什么时候该用它?
当你写的任务是有时间限制的,比如网络请求超时、用户主动取消操作、后台定时任务清理资源,这时候用 lockInterruptibly() 就很有必要。它让程序更灵活,也能避免资源浪费。
比如你开发一个文件上传功能,用户点了“取消”。如果上传线程正卡在某个锁上等,而这个锁不支持中断,那线程还得继续等下去,直到拿到锁才可能检查到取消标志——用户体验就很差。但如果用了可中断锁,主线程一调 interrupt(),工作线程立马就能响应,马上停下来。
注意点
不是所有场景都适合用 lockInterruptibly()。如果中断处理逻辑没写好,可能导致锁没释放或者任务状态混乱。记得一定要在 catch 中做好资源清理,尤其是 unlock() 别漏掉。
另外,普通 lock() 方法是不可中断的,只有显式调用 lockInterruptibly() 才具备这个能力。别以为用了 ReentrantLock 就自动有中断支持,得用对方法才行。