java如何發(fā)現(xiàn)代碼死鎖 java解決死鎖的三種方法

如何通過編程發(fā)現(xiàn)Java死鎖

bootstrap-table是在bootstrap-table的基礎(chǔ)上寫出來的,專門用于顯示數(shù)據(jù)的表格插件。而bootstrap是來自 Twitter,是目前最受歡迎的前端框架。Bootstrap 是基于 HTML、CSS、JAVASCRIPT 的,具有簡便靈活,快速前端開發(fā)的優(yōu)勢。對與bootstrap在此就不在敘述。本文將著重講解自己在項目中使用到bootstrap-table的一些理解和如何學(xué)習(xí)它。

10年積累的網(wǎng)站設(shè)計、網(wǎng)站建設(shè)經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先做網(wǎng)站后付款的網(wǎng)站建設(shè)流程,更有寧國免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

首先交代一下,jquery ,bootstrap ,bootstrap-table 三者之間的關(guān)系。bootstrap很多部分代碼涉及到了jquery的,也就是說 bootstrap是依賴jquery的,而我們要使用的bootstrap-table則是在bootstrap基礎(chǔ)上創(chuàng)造出來的,所以在使用bootstrap-table之前必須引用 jquery 和bootstrap的相關(guān)js,css文件。

接著說,bootstrap-table的特點:與jquery-ui,jqgrid等表格顯示插件而言,bootstrap-table扁平化,輕量級,對于一些輕量級的數(shù)據(jù)顯示,他是綽綽有余,而對父子表等的支持也很好,最主要的是可以與bootstrap的其他標(biāo)簽無縫組合。

Java開發(fā)中,什么是死鎖(Deadlock)?如何分析和避免死鎖?

死鎖是指兩個以上的線程永遠(yuǎn)阻塞的情況,這種情況產(chǎn)生至少需要兩個以上的線程和兩個以上的資源。

分析死鎖,需要查看Java應(yīng)用程序的線程轉(zhuǎn)儲,需要找出那些狀態(tài)為BLOCKED的線程和他們等待的資源。每個資源都有一個唯一的id,用這個id可以找出哪些線程已經(jīng)擁有了它的對象鎖。

避免嵌套鎖,只在需要的地方使用鎖和避免無限期等待是避免死鎖的通常辦法。

JAVA多線程死鎖問題

1. Java中導(dǎo)致死鎖的原因

Java中死鎖最簡單的情況是,一個線程T1持有鎖L1并且申請獲得鎖L2,而另一個線程T2持有鎖L2并且申請獲得鎖L1,因為默認(rèn)的鎖申請操作都是阻塞的,所以線程T1和T2永遠(yuǎn)被阻塞了。導(dǎo)致了死鎖。這是最容易理解也是最簡單的死鎖的形式。但是實際環(huán)境中的死鎖往往比這個復(fù)雜的多??赡軙卸鄠€線程形成了一個死鎖的環(huán)路,比如:線程T1持有鎖L1并且申請獲得鎖L2,而線程T2持有鎖L2并且申請獲得鎖L3,而線程T3持有鎖L3并且申請獲得鎖L1,這樣導(dǎo)致了一個鎖依賴的環(huán)路:T1依賴T2的鎖L2,T2依賴T3的鎖L3,而T3依賴T1的鎖L1。從而導(dǎo)致了死鎖。

從這兩個例子,我們可以得出結(jié)論,產(chǎn)生死鎖可能性的最根本原因是:線程在獲得一個鎖L1的情況下再去申請另外一個鎖L2,也就是鎖L1想要包含了鎖L2,也就是說在獲得了鎖L1,并且沒有釋放鎖L1的情況下,又去申請獲得鎖L2,這個是產(chǎn)生死鎖的最根本原因。另一個原因是默認(rèn)的鎖申請操作是阻塞的。

2. Java中如何避免死鎖

既然我們知道了產(chǎn)生死鎖可能性的原因,那么就可以在編碼時進(jìn)行規(guī)避。Java是面向?qū)ο蟮木幊陶Z言,程序的最小單元是對象,對象封裝了數(shù)據(jù)和操作,所以Java中的鎖一般也是以對象為單位的,對象的內(nèi)置鎖保護(hù)對象中的數(shù)據(jù)的并發(fā)訪問。所以如果我們能夠避免在對象的同步方法中調(diào)用其它對象的同步方法,那么就可以避免死鎖產(chǎn)生的可能性。如下所示的代碼,就存在死鎖的可能性:

public class ClassB {

private String address;

// ...

public synchronized void method1(){

// do something

}

// ... ...

}

public class ClassA {

private int id;

private String name;

private ClassB b;

// ...

public synchronized void m1(){

// do something

b.method1();

}

// ... ...

}

上面的ClassA.m1()方法,在對象的同步方法中又調(diào)用了ClassB的同步方法method1(),所以存在死鎖發(fā)生的可能性。我們可以修改如下,避免死鎖:

public class ClassA {

private int id;

private String name;

private ClassB b;

// ...

public void m2(){

synchronized(this){

// do something

}

b.method1();

}

// ... ...

}

這樣的話減小了鎖定的范圍,兩個鎖的申請就沒有發(fā)生交叉,避免了死鎖的可能性,這是最理性的情況,因為鎖沒有發(fā)生交叉。但是有時是不允許我們這樣做的。此時,如果只有ClassA中只有一個m1這樣的方法,需要同時獲得兩個對象上的鎖,并且不會將實例屬性 b 溢出(return b;),而是將實例屬性 b 封閉在對象中,那么也不會發(fā)生死鎖。因為無法形成死鎖的閉環(huán)。但是如果ClassA中有多個方法需要同時獲得兩個對象上的鎖,那么這些方法就必須以相同的順序獲得鎖。

比如銀行轉(zhuǎn)賬的場景下,我們必須同時獲得兩個賬戶上的鎖,才能進(jìn)行操作,兩個鎖的申請必須發(fā)生交叉。這時我們也可以打破死鎖的那個閉環(huán),在涉及到要同時申請兩個鎖的方法中,總是以相同的順序來申請鎖,比如總是先申請 id 大的賬戶上的鎖 ,然后再申請 id 小的賬戶上的鎖,這樣就無法形成導(dǎo)致死鎖的那個閉環(huán)。

public class Account {

private int id; // 主鍵

private String name;

private double balance;

public void transfer(Account from, Account to, double money){

if(from.getId() to.getId()){

synchronized(from){

synchronized(to){

// transfer

}

}

}else{

synchronized(to){

synchronized(from){

// transfer

}

}

}

}

public int getId() {

return id;

}

}

這樣的話,即使發(fā)生了兩個賬戶比如 id=1的和id=100的兩個賬戶相互轉(zhuǎn)賬,因為不管是哪個線程先獲得了id=100上的鎖,另外一個線程都不會去獲得id=1上的鎖(因為他沒有獲得id=100上的鎖),只能是哪個線程先獲得id=100上的鎖,哪個線程就先進(jìn)行轉(zhuǎn)賬。這里除了使用id之外,如果沒有類似id這樣的屬性可以比較,那么也可以使用對象的hashCode()的值來進(jìn)行比較。

上面我們說到,死鎖的另一個原因是默認(rèn)的鎖申請操作是阻塞的,所以如果我們不使用默認(rèn)阻塞的鎖,也是可以避免死鎖的。我們可以使用ReentrantLock.tryLock()方法,在一個循環(huán)中,如果tryLock()返回失敗,那么就釋放以及獲得的鎖,并睡眠一小段時間。這樣就打破了死鎖的閉環(huán)。

比如:線程T1持有鎖L1并且申請獲得鎖L2,而線程T2持有鎖L2并且申請獲得鎖L3,而線程T3持有鎖L3并且申請獲得鎖L1

此時如果T3申請鎖L1失敗,那么T3釋放鎖L3,并進(jìn)行睡眠,那么T2就可以獲得L3了,然后T2執(zhí)行完之后釋放L2, L3,所以T1也可以獲得L2了執(zhí)行完然后釋放鎖L1, L2,然后T3睡眠醒來,也可以獲得L1, L3了。打破了死鎖的閉環(huán)。

這些情況,都還是比較好處理的,因為它們都是相關(guān)的,我們很容易意識到這里有發(fā)生死鎖的可能性,從而可以加以防備。很多情況的場景都不會很明顯的讓我們察覺到會存在發(fā)生死鎖的可能性。所以我們還是要注意:

一旦我們在一個同步方法中,或者說在一個鎖的保護(hù)的范圍中,調(diào)用了其它對象的方法時,就要十而分的小心:

1)如果其它對象的這個方法會消耗比較長的時間,那么就會導(dǎo)致鎖被我們持有了很長的時間;

2)如果其它對象的這個方法是一個同步方法,那么就要注意避免發(fā)生死鎖的可能性了;

最好是能夠避免在一個同步方法中調(diào)用其它對象的延時方法和同步方法。如果不能避免,就要采取上面說到的編碼技巧,打破死鎖的閉環(huán),防止死鎖的發(fā)生。同時我們還可以盡量使用“不可變對象”來避免鎖的使用,在某些情況下還可以避免對象的共享,比如 new 一個新的對象代替共享的對象,因為鎖一般是對象上的,對象不相同了,也就可以避免死鎖,另外盡量避免使用靜態(tài)同步方法,因為靜態(tài)同步相當(dāng)于全局鎖。還有一些封閉技術(shù)可以使用:比如堆棧封閉,線程封閉,ThreadLocal,這些技術(shù)可以減少對象的共享,也就減少了死鎖的可能性。

本文標(biāo)題:java如何發(fā)現(xiàn)代碼死鎖 java解決死鎖的三種方法
網(wǎng)站URL:http://muchs.cn/article8/dohdpip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、響應(yīng)式網(wǎng)站微信公眾號、虛擬主機(jī)、網(wǎng)站建設(shè)手機(jī)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)