新聞中心
JAVA 死鎖問題 下面的代碼 如何改動能避免死鎖
把synchronized(second) {}放到synchronized(first) {}的外面去,就不會死鎖了
目前創(chuàng)新互聯(lián)建站已為上千的企業(yè)提供了網(wǎng)站建設(shè)、域名、雅安服務(wù)器托管、網(wǎng)站托管、服務(wù)器租用、企業(yè)網(wǎng)站設(shè)計、遂寧網(wǎng)站維護等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
JAVA程序設(shè)計,多線程且避免死鎖
JAVA中幾種常見死鎖及對策:解決死鎖沒有簡單的方法,這是因為線程產(chǎn)生死鎖都各有各的原因,而且往往具有很高的負載。大多數(shù)軟件測試產(chǎn)生不了足夠多的負載,所以不可能暴露所有的線程錯誤。在這里中,下面將討論開發(fā)過程常見的4類典型的死鎖和解決對策。(1)數(shù)據(jù)庫死鎖在數(shù)據(jù)庫中,如果一個連接占用了另一個連接所需的數(shù)據(jù)庫鎖,則它可以阻塞另一個連接。如果兩個或兩個以上的連接相互阻塞,則它們都不能繼續(xù)執(zhí)行,這種情況稱為數(shù)據(jù)庫死鎖。數(shù)據(jù)庫死鎖問題不易處理,通常數(shù)據(jù)行進行更新時,需要鎖定該數(shù)據(jù)行,執(zhí)行更新,然后在提交或回滾封閉事務(wù)時釋放鎖。由于數(shù)據(jù)庫平臺、配置的隔離級以及查詢提示的不同,獲取的鎖可能是細粒度或粗粒度的,它會阻塞(或不阻塞)其他對同一數(shù)據(jù)行、表或數(shù)據(jù)庫的查詢?;跀?shù)據(jù)庫模式,讀寫操作會要求遍歷或更新多個索引、驗證約束、執(zhí)行觸發(fā)器等。每個要求都會引入鎖。此外,其他應(yīng)用程序還可能正在訪問同一數(shù)據(jù)庫模式中的某些對象,并獲取不同應(yīng)用程序所具有的鎖。所有這些因素綜合在一起,數(shù)據(jù)庫死鎖幾乎不可能被消除了。值得慶幸的是,數(shù)據(jù)庫死鎖通常是可恢復(fù)的:當數(shù)據(jù)庫發(fā)現(xiàn)死鎖時,它會強制銷毀一個連接(通常是使用最少的連接),并回滾其事務(wù)。這將釋放所有與已經(jīng)結(jié)束的事務(wù)相關(guān)聯(lián)的鎖,至少允許其他連接中有一個可以獲取它們正在被阻塞的鎖。由于數(shù)據(jù)庫具有這種典型的死鎖處理行為,所以當出現(xiàn)數(shù)據(jù)庫死鎖問題時,數(shù)據(jù)庫常常只能重試整個事務(wù)。當數(shù)據(jù)庫連接被銷毀時,會拋出可被應(yīng)用程序捕獲的異常,并標識為數(shù)據(jù)庫死鎖。如果允許死鎖異常傳播到初始化該事務(wù)的代碼層之外,則該代碼層可以啟動一個新事務(wù)并重做先前所有工作。當出現(xiàn)問題就重試,由于數(shù)據(jù)庫可以自由地獲取鎖,所以幾乎不可能保證兩個或兩個以上的線程不發(fā)生數(shù)據(jù)庫死鎖。此方法至少能保證在出現(xiàn)某些數(shù)據(jù)庫死鎖情況時,應(yīng)用程序能正常運行。(2)資源池耗盡死鎖客戶端的增加導(dǎo)致資源池耗盡死鎖是由于負載而造成的,即資源池太小,而每個線程需要的資源超過了池中的可用資源。假設(shè)連接池最多有10個連接,同時有10個對外部并發(fā)調(diào)用。這些線程中每一個都需要一個數(shù)據(jù)庫連接用來清空池?,F(xiàn)在,每個線程都執(zhí)行嵌套的調(diào)用。則所有線程都不能繼續(xù),但又都不放棄自己的第一個數(shù)據(jù)庫連接。這樣,10個線程都將被死鎖。研究此類死鎖,會發(fā)現(xiàn)線程存儲中有大量等待獲取資源的線程,以及同等數(shù)量的空閑且未阻塞的活動數(shù)據(jù)庫連接。當應(yīng)用程序死鎖時,如果可以在運行時檢測連接池,就能確認連接池實際上已空。修復(fù)此類死鎖的方法包括:增加連接池的大小或者重構(gòu)代碼,以便單個線程不需要同時使用很多數(shù)據(jù)庫連接?;蛘呖梢栽O(shè)置內(nèi)部調(diào)用使用不同的連接池,即使外部調(diào)用的連接池為空,內(nèi)部調(diào)用也能使用自己的連接池繼續(xù)。(3)單線程、多沖突數(shù)據(jù)庫連接死鎖對同一線程執(zhí)行嵌套的調(diào)用有時出現(xiàn)死鎖,此情形即使在非高負載系統(tǒng)中通常也會發(fā)生。當?shù)谝粋€(外部)連接已獲取第二個(內(nèi)部)連接所需要的數(shù)據(jù)庫鎖,則第二個連接將永久阻塞第一個連接,并等待第一個連接被提交或回滾,這就出現(xiàn)了死鎖情形。因為數(shù)據(jù)庫沒有注意到兩個連接之間的關(guān)系,所以數(shù)據(jù)庫不會將此情形檢測為死鎖。這樣即使不存在并發(fā),此代碼也將導(dǎo)致死鎖。此情形有多種具體的變種,可以涉及多個線程和兩個以上的數(shù)據(jù)庫連接。(4)Java虛擬機鎖與數(shù)據(jù)庫鎖沖突這種情形發(fā)生在數(shù)據(jù)庫鎖與Java虛擬機鎖并存的時候。在這種情況下,一個線程占有一個數(shù)據(jù)庫鎖并嘗試獲取Java虛擬機鎖。同時,另一個線程占有Java虛擬機鎖并嘗試獲取數(shù)據(jù)庫鎖。此時,數(shù)據(jù)庫發(fā)現(xiàn)一個連接阻塞了另一個連接,但由于無法阻止連接繼續(xù),所以不會檢測到死鎖。Java虛擬機發(fā)現(xiàn)同步的鎖中有一個線程,并有另一個嘗試進入的線程,所以即使Java虛擬機能檢測到死鎖并對它們進行處理,它還是不會檢測到這種情況?!】偠灾?,JAVA應(yīng)用程序中的死鎖是一個大問題——它能導(dǎo)致整個應(yīng)用程序慢慢終止,還很難被分離和修復(fù),尤其是當開發(fā)人員不熟悉如何分析死鎖環(huán)境的時候。五.死鎖的經(jīng)驗法則筆者在開發(fā)中總結(jié)以下死鎖問題的經(jīng)驗。(1)對大多數(shù)的Java程序員來說最簡單的防止死鎖的方法是對競爭的資源引入序號,如果一個線程需要幾個資源,那么它必須先得到小序號的資源,再申請大序號的資源。可以在Java代碼中增加同步關(guān)鍵字的使用,這樣可以減少死鎖,但這樣做也會影響性能。如果負載過重,數(shù)據(jù)庫內(nèi)部也有可能發(fā)生死鎖。(2)了解數(shù)據(jù)庫鎖的發(fā)生行為。假定任何數(shù)據(jù)庫訪問都有可能陷入數(shù)據(jù)庫死鎖狀況,但是都能正確進行重試。例如了解如何從應(yīng)用服務(wù)器獲取完整的線程轉(zhuǎn)儲以及從數(shù)據(jù)庫獲取數(shù)據(jù)庫連接列表(包括互相阻塞的連接),知道每個數(shù)據(jù)庫連接與哪個Java線程相關(guān)聯(lián)。了解Java線程和數(shù)據(jù)庫連接之間映射的最簡單方法是向連接池訪問模式添加日志記錄功能。(3)當進行嵌套的調(diào)用時,了解哪些調(diào)用使用了與其它調(diào)用同樣的數(shù)據(jù)庫連接。即使嵌套調(diào)用運行在同一個全局事務(wù)中,它仍將使用不同的數(shù)據(jù)庫連接,而不會導(dǎo)致嵌套死鎖。(4)確保在峰值并發(fā)時有足夠大的資源池。(5)避免執(zhí)行數(shù)據(jù)庫調(diào)用或在占有Java虛擬機鎖時,執(zhí)行其他與Java虛擬機無關(guān)的操作?!∽钪匾氖?,多線程設(shè)計雖然是困難的,但在開始編程之前詳細設(shè)計系統(tǒng)能夠幫助你避免難以發(fā)現(xiàn)死鎖的問題。死鎖在語言層面上不能解決,就需要一個良好設(shè)計來避免死鎖。
北大青鳥java培訓(xùn):如何避免死鎖?
什么是死鎖,如何避免死鎖?線程A需要資源X,而線程B需要資源Y,而雙方都掌握有對方所要的資源,這種情況稱為死鎖(deadlock),或死亡擁抱(thedeadlyembrace)。
在并發(fā)程序設(shè)計中,甘肅電腦培訓(xùn)建議死鎖(deadlock)是一種十分常見的邏輯錯誤。
通過采用正確的編程方式,死鎖的發(fā)生不難避免。
死鎖的四個必要條件在計算機專業(yè)的教材中,通常都會介紹死鎖的四個必要條件。
這四個條件缺一不可,或者說只要破壞了其中任何一個條件,死鎖就不可能發(fā)生。
我們來復(fù)習(xí)一下,這四個條件是:互斥(Mutualexclusion):存在這樣一種資源,它在某個時刻只能被分配給一個執(zhí)行緒(也稱為線程)使用;持有(Holdandwait):當請求的資源已被占用從而導(dǎo)致執(zhí)行緒阻塞時,資源占用者不但無需釋放該資源,而且還可以繼續(xù)請求更多資源;不可剝奪(Nopreemption):執(zhí)行緒獲得到的互斥資源不可被強行剝奪,換句話說,只有資源占用者自己才能釋放資源;環(huán)形等待(Circularwait):若干執(zhí)行緒以不同的次序獲取互斥資源,從而形成環(huán)形等待的局面,想象在由多個執(zhí)行緒組成的環(huán)形鏈中,每個執(zhí)行緒都在等待下一個執(zhí)行緒釋放它持有的資源。
解除死鎖的必要條件不難看出,在死鎖的四個必要條件中,第二、三和四項條件比較容易消除。
通過引入事務(wù)機制,往往可以消除第二、三兩項條件,方法是將所有上鎖操作均作為事務(wù)對待,一旦開始上鎖,即確保全部操作均可回退,同時通過鎖管理器檢測死鎖,并剝奪資源(回退事務(wù))。
這種做法有時會造成較大開銷,而且也需要對上鎖模式進行較多改動。
消除第四項條件是比較容易且代價較低的辦法。
具體來說這種方法約定:上鎖的順序必須一致。
具體來說,我們?nèi)藶榈亟o鎖指定一種類似“水位”的方向性屬性。
無論已持有任何鎖,該執(zhí)行緒所有的上鎖操作,必須按照一致的先后順序從低到高(或從高到低)進行,且在一個系統(tǒng)中,只允許使用一種先后次序。
請注意,放鎖的順序并不會導(dǎo)致死鎖。
也就是說,盡管按照鎖A,鎖B,放A,放B這樣的順序來進行鎖操作看上去有些怪異,但是只要大家都按先A后B的順序上鎖,便不會導(dǎo)致死鎖。
解決方法:1使用事務(wù)時,盡量縮短事務(wù)的邏輯處理過程,及早提交或回滾事務(wù)(細化處理邏輯,執(zhí)行一段邏輯后便回滾或者提交,然后再執(zhí)行其它邏輯,直到事物執(zhí)行完畢提交);2設(shè)置死鎖超時參數(shù)為合理范圍,如:3分鐘-10分種;超過時間,自動放棄本次操作,避免進程懸掛;?3優(yōu)化程序,檢查并避免死鎖現(xiàn)象出現(xiàn);?4對所有的腳本和SP都要仔細測試,在正是版本之前。
5所有的SP都要有錯誤處理(通過@error)?6一般不要修改SQLSERVER事務(wù)的默認級別。
不推薦強行加鎖另外參考的解決方法:按同一順序訪問對象如果所有并發(fā)事務(wù)按同一順序訪問對象,則發(fā)生死鎖的可能性會降低。
例如,如果兩個并發(fā)事務(wù)獲得Supplier表上的鎖,然后獲得Part表上的鎖,則在其中一個事務(wù)完成之前,另一個事務(wù)被阻塞在Supplier表上。
第一個事務(wù)提交或回滾后,第二個事務(wù)繼續(xù)進行。
不發(fā)生死鎖。
將存儲過程用于所有的數(shù)據(jù)修改可以標準化訪問對象的順序。
如何避免Java線程死鎖
Java線程死鎖需要如何解決,這個問題一直在我們不斷的使用中需要只有不斷的關(guān)鍵。不幸的是,使用上鎖會帶來其他問題。讓我們來看一些常見問題以及相應(yīng)的解決方法: Java線程死鎖 Java線程死鎖是一個經(jīng)典的多線程問題,因為不同的線程都在等待那些根本不可能被釋放的鎖,從而導(dǎo)致所有的工作都無法完成。假設(shè)有兩個線程,分別代表兩個饑餓的人,他們必須共享刀叉并輪流吃飯。他們都需要獲得兩個鎖:共享刀和共享叉的鎖。 假如線程 “A”獲得了刀,而線程“B”獲得了叉。線程“A”就會進入阻塞狀態(tài)來等待獲得叉,而線程“B”則阻塞來等待“A”所擁有的刀。這只是人為設(shè)計的例子,但盡管在運行時很難探測到,這類情況卻時常發(fā)生。雖然要探測或推敲各種情況是非常困難的,但只要按照下面幾條規(guī)則去設(shè)計系統(tǒng),就能夠避免Java線程死鎖問題: 讓所有的線程按照同樣的順序獲得一組鎖。這種方法消除了 X 和 Y 的擁有者分別等待對方的資源的問題。 將多個鎖組成一組并放到同一個鎖下。前面Java線程死鎖的例子中,可以創(chuàng)建一個銀器對象的鎖。于是在獲得刀或叉之前都必須獲得這個銀器的鎖。 將那些不會阻塞的可獲得資源用變量標志出來。當某個線程獲得銀器對象的鎖時,就可以通過檢查變量來判斷是否整個銀器集合中的對象鎖都可獲得。如果是,它就可以獲得相關(guān)的鎖,否則,就要釋放掉銀器這個鎖并稍后再嘗試。 最重要的是,在編寫代碼前認真仔細地設(shè)計整個系統(tǒng)。多線程是困難的,在開始編程之前詳細設(shè)計系統(tǒng)能夠幫助你避免難以發(fā)現(xiàn)Java線程死鎖的問題。 Volatile 變量,volatile 關(guān)鍵字是 Java 語言為優(yōu)化編譯器設(shè)計的。以下面的代碼為例: 1.class VolatileTest {
2.public void foo() {
3.boolean flag = false;
4.if(flag) {
5.//this could happen
6.}
7.}
8.} 一個優(yōu)化的編譯器可能會判斷出if部分的語句永遠不會被執(zhí)行,就根本不會編譯這部分的代碼。如果這個類被多線程訪問, flag被前面某個線程設(shè)置之后,在它被if語句測試之前,可以被其他線程重新設(shè)置。用volatile關(guān)鍵字來聲明變量,就可以告訴編譯器在編譯的時候,不需要通過預(yù)測變量值來優(yōu)化這部分的代碼。 無法訪問的Java線程死鎖有時候雖然獲取對象鎖沒有問題,線程依然有可能進入阻塞狀態(tài)。在 Java 編程中IO就是這類問題最好的例子。當線程因為對象內(nèi)的IO調(diào)用而阻塞時,此對象應(yīng)當仍能被其他線程訪問。該對象通常有責任取消這個阻塞的IO操作。造成阻塞調(diào)用的線程常常會令同步任務(wù)失敗。如果該對象的其他方法也是同步的,當線程被阻塞時,此對象也就相當于被冷凍住了。 其他的線程由于不能獲得對象的Java線程死鎖,就不能給此對象發(fā)消息(例如,取消 IO 操作)。必須確保不在同步代碼中包含那些阻塞調(diào)用,或確認在一個用同步阻塞代碼的對象中存在非同步方法。盡管這種方法需要花費一些注意力來保證結(jié)果代碼安全運行,但它允許在擁有對象的線程發(fā)生阻塞后,該對象仍能夠響應(yīng)其他線程。 編輯推薦: 1. Java多線程優(yōu)化之偏向鎖原理分析 2. Java多線程實現(xiàn)異步調(diào)用的方法 3. 使用Java多線程機制實現(xiàn)下載的方法介紹
當前標題:java避免死鎖代碼 java解決死鎖
分享URL:http://biofuelwatch.net/article/ddgigpj.html