版權聲明
- 本文原創作者:清風不渡
- 博客地址:https://blog.csdn.net/WXKKang
上面我們講到了Java中線程互斥的機制,那就是給線程訪問的數據上鎖,那麼這樣就一定安全嗎?答案肯定是否定的,這個世界上沒有絕對安全的東西,而上篇所講到的方法就會出現線程死鎖的現象,那麼這個現象是什麼意思呢?我們該怎樣處理或者說怎樣避免發生這樣的現象呢?下面我們就來一一學習吧
一、線程死鎖
多線程中使用鎖會造成效率低,如果出現了同步嵌套,就容易產生死鎖問題
死鎖是指兩個或者兩個以上的線程在執行的過程中,因爭奪資源產生的一種互相等待現象
如果兩個線程之間有多個共享對象,如果兩個線程分別持有對方所需要的共享對象的鎖,在試圖獲取對方線程所持有的共享對象的鎖時,就有死鎖的危險
1、死鎖產生的原因
加入我們現在有兩個線程(線程1與線程2),這兩個線程之間有兩個共享對象(對象A和對象B),在線程執行的過程中,線程1與線程2都必須同時獲得對象A與對象B後才能正常工作
那麼當線程1獲得了對象A的鎖、線程2獲得了對象B的鎖,如果兩個線程都不釋放所獲得的對象的鎖,那麼線程1將不能得到對象B的鎖,線程2將不能得到對象A的鎖,這種情況下兩個線程都將被阻塞,即所謂的死鎖
2、死鎖的解決方案
死鎖通常是由於資源的無序使用,導致線程不能得到正常執行所需要的資源
解決死鎖問題的方法,就是所有線程使用一致的順序申請資源鎖。例如:
1、線程1和線程2都是按照對象A、對象B的順序來獲得對象鎖
2、將對象A編號爲1,對象B編號爲2,線程1和線程2都必須先獲得1號資源後方可再獲取2號資源
3、死鎖示例
下面我們來演示一個發生死鎖的例子,這裏有四個對象:藝術家、畫匠、筆、紙。藝術家、畫匠在工作時都需要同時有筆、紙 死鎖演示效果:
1、藝術家首先得到筆,然後再去獲取紙;工匠首先得到紙,然後再去獲取筆
2、由於藝術家和工匠都不願意放棄自己獲得的筆、紙,因此二者都不能同時獲得筆和紙,因此都無法工作
代碼如下:
package qfbd.com;
/*
原創作者:清風不渡
博客地址:https://blog.csdn.net/WXKKang
*/
public class Demo {
public static void main(String[] args) throws Exception {
Resource resource = new Resource();
//創建藝術家對象
Artist artist = new Artist();
artist.prepace(resource);
//創建工匠
Limner limner = new Limner();
limner.prepace(resource);
//這裏一定要先啓動藝術家纔能有死鎖情況
artist.start();
limner.start();
}
}
//筆類
class Pen{
}
//紙類
class Paper{
}
//共享的資源類,這裏同時有筆和紙
class Resource{
private Pen pen = new Pen();
private Paper paper = new Paper();
public Paper getPaper(){
return paper;
}
public Pen getPen(){
return pen;
}
}
/*
* 藝術家
*
* 這是一個線程類,在線程體中有兩個同步塊,分別是對pen和paper對象進行同步
*/
class Artist extends Thread {
private Resource resource;
@Override
public void run() {
Pen pen = resource.getPen();
System.out.println("藝術家獲得筆!!!");
synchronized (pen) {
/*
* 加入睡眠原因:
* 1、在運行線程時,藝術家線程先執行,他首先獲得筆
* 2、如果沒有這個睡眠,那麼可能畫匠還沒來的及獲取到紙,
* 藝術家線程就獲取到了紙,這樣就無法測試死鎖的情形了
*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("藝術家睡眠發生異常!!!");
}
//睡眠之後獲取紙,但預測紙已經被畫匠獲取到了,此刻進入等待
Paper paper = resource.getPaper();
synchronized (paper) {
System.out.println("藝術家可以畫畫了!!");
}
}
}
public void prepace(Resource resource){
this.resource = resource;
}
}
/*
* 畫匠
*
* 這是一個線程類,在線程體中有兩個同步塊,分別是對paper和pen對象進行同步
* 注意:他的同步對象paper和pen的順序和藝術家是相反的
*/
class Limner extends Thread{
private Resource resource;
@Override
public void run() {
Paper paper = resource.getPaper();
System.out.println("畫匠得到紙!!!");
synchronized (paper) {
//然後獲取筆,預測被藝術家得到並睡眠,此刻進入等待
Pen pen = resource.getPen();
synchronized (pen) {
System.out.println("畫匠可以畫畫了!!!");
}
}
}
public void prepace(Resource resource){
this.resource = resource;
}
}
執行結果如下:
可以發現,程序運行“卡死了”,即發生了所謂的死鎖現象