开宗明义:这两个方法的核心区别在于当线程在等待获取锁时,是否响应中断(Interrupt)。

1.lock()方法
- 特性:不可中断。
- 行为:当一个线程调用 lock() 方法时,如果锁已经被其他线程持有,它会一直阻塞(block),直到它成功获取到锁。在这个阻塞过程中,即使其他线程调用了它的 interrupt() 方法,它也不会立即返回或抛出异常,而是继续等待锁。中断的状态会被记录,当它最终获取到锁后,才会感受到这个中断(例如,在后续的 sleep() 或 wait() 调用中抛出 InterruptedException)。
2.lockInterruptibly()方法
- 特性:可中断。
- 行为:当一个线程调用 lockInterruptibly() 方法时,如果锁已经被其他线程持有,它会阻塞等待。但是,与 lock() 不同,如果在阻塞期间有其他线程调用了它的 interrupt() 方法,它会立即抛出 InterruptedException 异常,并终止等待锁的过程。这允许线程在等待锁的过程中被 “唤醒” 并处理中断逻辑。
代码示例
假设我们有一个场景:线程 A 先获取了锁,然后休眠 5 秒。线程 B 在这期间尝试获取同一个锁。
使用lock()(不可中断)
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(() -> {
lock.lock(); // 线程A获取锁
try {
System.out.println("线程A: 获取了锁,开始休眠5秒...");
Thread.sleep(5000); // 持有锁休眠
System.out.println("线程A: 休眠结束,释放锁。");
} catch (InterruptedException e) {
System.out.println("线程A: 在休眠时被中断了。");
Thread.currentThread().interrupt(); // 恢复中断状态
} finally {
lock.unlock(); // 确保锁被释放
}
});
Thread threadB = new Thread(() -> {
System.out.println("线程B: 尝试获取锁...");
lock.lock(); // 线程B会在这里阻塞,即使被中断也不会退出
try {
System.out.println("线程B: 成功获取了锁!");
} finally {
lock.unlock();
}
});
threadA.start();
Thread.sleep(1000); // 确保线程A先获取到锁
threadB.start();
Thread.sleep(2000); // 等待2秒
System.out.println("主线程: 尝试中断线程B...");
threadB.interrupt(); // 中断线程B
threadA.join();
threadB.join();
}
}
运行结果分析:
- 线程 A 获取锁并休眠。
- 线程 B 调用 lock(),开始阻塞。
- 主线程在 2 秒后中断线程 B。
- 线程 B不会由于中断而停止等待,它会继续阻塞,直到线程 A 在 5 秒后释放锁。
- 线程 B 最终会获取到锁,并打印 “线程 B: 成功获取了锁!”。中断请求被忽略了。
使用lockInterruptibly()(可中断)
import java.util.concurrent.locks.ReentrantLock;
public class LockInterruptiblyExample {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(() -> {
lock.lock();
try {
System.out.println("线程A: 获取了锁,开始休眠5秒...");
Thread.sleep(5000);
System.out.println("线程A: 休眠结束,释放锁。");
} catch (InterruptedException e) {
System.out.println("线程A: 在休眠时被中断了。");
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
});
Thread threadB = new Thread(() -> {
System.out.println("线程B: 尝试获取锁 (可中断)...");
try {
lock.lockInterruptibly(); // 在这里等待,如果被中断会抛出异常
try {
System.out.println("线程B: 成功获取了锁!");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("线程B: 在等待锁时被中断了!");
Thread.currentThread().interrupt(); // 恢复中断状态
}
});
threadA.start();
Thread.sleep(1000);
threadB.start();
Thread.sleep(2000);
System.out.println("主线程: 尝试中断线程B...");
threadB.interrupt();
threadA.join();
threadB.join();
}
}
运行结果分析:
- 线程 A 获取锁并休眠。
- 线程 B 调用 lockInterruptibly(),开始阻塞。
- 主线程在 2 秒后中断线程 B。
- 线程 B 在阻塞期间收到中断信号,立即抛出 InterruptedException。
- 线程 B 的 catch 块捕获到异常,并打印 “线程 B: 在等待锁时被中断了!”。它没有获取到锁。
核心区别对比
|
特性 |
lock() |
lockInterruptibly() |
|
响应中断 |
不响应。线程会一直阻塞直到获取锁。 |
响应。线程在阻塞等待时如果收到中断信号,会立即抛出 InterruptedException 并退出。 |
|
异常抛出 |
不抛出 InterruptedException。 |
声明抛出 InterruptedException,调用者必须处理(捕获或抛出)。 |
|
适用场景 |
当你希望线程必须等到锁,不希望被外部因素打断时。 |
当你希望线程在等待锁的过程中能够响应中断,例如实现超时等待、或者需要能够撤销任务时。这是一种更灵活、更安全的设计。 |
|
性能 |
一般来说,lock() 的性能会略高于 lockInterruptibly(),由于它不需要处理中断检查的逻辑。 |
性能上可能会有微小的开销,由于需要在阻塞时检查中断状态。 |
总结
- lock():是一个 “强硬” 的锁获取方式,一旦开始等待,就必须等到锁为止。
- lockInterruptibly():是一个 “更合作” 的锁获取方式,它允许线程在等待期间被中断,从而可以优雅地退出等待,这在构建可撤销的任务或服务时超级有用。
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END











暂无评论内容