什麼是可中斷鎖?有什麼用?怎麼實現?
作者 | 王磊
來源 | Java 中文社羣(ID:javacn666)
在 Java 中有兩種鎖,一種是內置鎖 synchronized,一種是顯示鎖 Lock,其中 Lock 鎖是可中斷鎖,而 synchronized 則爲不可中斷鎖。
所謂的中斷鎖指的是鎖在執行時可被中斷,也就是在執行時可以接收 interrupt 的通知,從而中斷鎖執行。
PS:默認情況下 Lock 也是不可中斷鎖,但是可以通過特殊的 “手段”,可以讓其變爲可中斷鎖,接下來我們一起來看。
爲什麼需要可中斷鎖?
不可中斷鎖的問題是,當出現 “異常” 時,只能一直阻塞等待,別無其他辦法,比如下面這個程序。下面的這個程序中有兩個線程,其中線程 1 先獲取到鎖資源執行相應代碼,而線程 2 在 0.5s 之後開始嘗試獲取鎖資源,但線程 1 執行時忘記釋放鎖了,這就造成線程 2 一直阻塞等待的情況,實現代碼如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
publicclass InterruptiblyExample {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
// 創建線程 1
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
System.out.println("線程 1:獲取到鎖.");
// 線程 1 未釋放鎖
}
});
t1.start();
// 創建線程 2
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// 先休眠 0.5s,讓線程 1 先執行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 獲取鎖
System.out.println("線程 2:等待獲取鎖.");
lock.lock();
try {
System.out.println("線程 2:獲取鎖成功.");
} finally {
lock.unlock();
}
}
});
t2.start();
}
}
以上代碼執行的結果如下:
並且,當我們熟練地拿出了 JConsole,試圖得到一個死鎖的具體信息時,卻得到了這樣的結果:
使用中斷鎖
然而,中斷鎖的出現,就可以打破這一僵局,它可以在等待一定時間之後,主動的中斷線程 2,以解決線程阻塞等待的問題。
中斷鎖的核心實現代碼是 lock.lockInterruptibly() 方法,它和 lock.lock() 方法作用類似,只不過使用 lockInterruptibly 方法可以優先接收中斷的請求,中斷鎖的具體實現如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
publicclass InterruptiblyExample {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
// 創建線程 1
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
// 加鎖操作
lock.lock();
System.out.println("線程 1:獲取到鎖.");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 線程 1 未釋放鎖
}
});
t1.start();
// 創建線程 2
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// 先休眠 0.5s,讓線程 1 先執行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 獲取鎖
try {
System.out.println("線程 2:嘗試獲取鎖.");
lock.lockInterruptibly(); // 可中斷鎖
System.out.println("線程 2:獲取鎖成功.");
} catch (InterruptedException e) {
System.out.println("線程 2:執行已被中斷.");
}
}
});
t2.start();
// 等待 2s 後,終止線程 2
Thread.sleep(2000);
if (t2.isAlive()) { // 線程 2 還在執行
System.out.println("執行線程的中斷.");
t2.interrupt();
} else {
System.out.println("線程 2:執行完成.");
}
}
}
以上代碼執行結果如下:
但當我們嘗試將 lockInterruptibly 方法換成 lock 方法之後(其他代碼都不變),執行的結果就完全不一樣了,實現代碼如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
publicclass InterruptiblyExample {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
// 創建線程 1
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
// 加鎖操作
lock.lockInterruptibly();
System.out.println("線程 1:獲取到鎖.");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 線程 1 未釋放鎖
}
});
t1.start();
// 創建線程 2
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// 先休眠 0.5s,讓線程 1 先執行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 獲取鎖
try {
System.out.println("線程 2:嘗試獲取鎖.");
lock.lock();
System.out.println("線程 2:獲取鎖成功.");
} catch (Exception e) {
System.out.println("線程 2:執行已被中斷.");
}
}
});
t2.start();
// 等待 2s 後,終止線程 2
Thread.sleep(2000);
if (t2.isAlive()) { // 線程 2 還在執行
System.out.println("執行線程的中斷.");
t2.interrupt();
} else {
System.out.println("線程 2:執行完成.");
}
}
}
以上程序執行結果如下:
總結
本文介紹了中斷鎖的實現,通過顯示鎖 Lock 的 lockInterruptibly 方法來完成,它和 lock 方法作用類似,但 lockInterruptibly 可以優先接收到中斷的通知,而 lock 方法只能 “死等” 鎖資源的釋放,同時這兩個方法的區別也是常見的面試題,希望本文對你有用。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/EqaWm7LZ-qesRbGvw0r8rQ