用最简单的语言形容就是,当一个线程去竞争资源的时候,会进入(阻塞/等待)队列,如果可以用一种方法,让它从(阻塞/等待)队列出来,就叫可打断锁。
这里大家容易混淆的就是synchronized中使用sleep和wait被打断了,就觉得synchronized是可打断锁,大家要明白一件事,你的sleep方法和wait方法激活的前提是,你已经获得了synchronized的对象锁了,这时候就意味着你并非在阻塞队列中。
只有在竞争锁的时候,在锁的(阻塞/等待)队列等待时,能被打断的锁,才能叫做可打断锁
下面代码证明synchronized并非可打断锁
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
Thread t1 = new Thread(() -> {
// 直接获得锁
synchronized (o) {
try {
System.out.println("t1获得锁了,开始休眠");
Thread.sleep(1000000); //长时间休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
try {
Thread.sleep(1000); //先休眠1s保证t1线程一定获得锁
} catch (InterruptedException e) {
e.printStackTrace();
}
// 1s后尝试获得锁,发现根本获得不到,会进入阻塞队列等待
synchronized (o) {
System.out.println("t2获得锁了");
}
});
Thread.sleep(2000); //休眠两秒后开始打断在阻塞队列的t2
System.out.println("打断t2,不让它继续等待");
t2.interrupt(); // 尝试将t2从阻塞队列打断,结果失效
}
ReentrantLock则是可打断锁
public class ReentrantLockTest {
// 定义锁对象
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
System.out.println("尝试获得锁");
// 如果没有竞争,获取锁,有竞争进入阻塞队列,可以被其它线程使用interrupt打断
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("没有获得锁,返回了");
return;
}
}, "t1");
lock.lock(); //主线程先锁
t1.start(); //这时候会一直等待并且可被打断
try {
Thread.sleep(1000); // 停留1s后,准备打断
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打断t1");
t1.interrupt();
}
}
这里我们也证明一件事,阻塞的队列是不能被打断的,只有等待的队列是可以的!