我們使用加鎖機制來確保執行緒安全,但如果過度地使用加鎖,則可能導致鎖順序死鎖。
當乙個執行緒永遠地持有乙個鎖,並且其他執行緒都嘗試獲得這個鎖時,那麼他們將永遠被阻塞。
在資料庫系統的設中考慮了檢測死鎖以及從死鎖中恢復。但它檢測到一組事務發生了死鎖時(通過在表示等待關係的有向圖中搜尋迴圈),將選擇乙個犧牲者並放棄這個事務
jvm在解決死鎖問題方面並沒有
服務那樣強大。當一組執行緒發生死鎖時,「遊戲」將到此結束。根據執行緒完成工作的不同,可能造成應用程式完全停止,或者某個特定的子系統停止。
死鎖的原因是:兩個執行緒試圖以不同的順序來獲得相同的鎖。
如果按照相同的順序來請求鎖,那麼就不會出現迴圈的加鎖依賴性,因此也就不會產生死鎖。
但是有時候,並不能清楚地知道是否在鎖順序上有足夠的控制權來避免死鎖的發生。
如考慮以下情景:
銀行轉賬行為,需要將資金由乙個賬戶轉入另乙個賬戶。在開始轉賬前要先獲得這兩個賬戶的acount物件的鎖,以確保通過原子的方式來更新兩個賬戶中的餘額。
public void transfermoney(acoount fromaccount,account toaccount,int amount)
}}
以上是執行緒不安全的,如果兩個執行緒同時呼叫transfermoney,其中乙個執行緒從x向y轉賬,另乙個執行緒從y向x轉賬,那麼就會發生死鎖。
解決方式是:可以使用system.identityhashcode,該方法將返回由object.hashcode返回的值。
修改以上**如下:
public void transfermoney(acoount fromaccount,account toaccount,int amount)
} }else
} }}
這樣無論傳入引數的位置如何,獲得鎖的順序都是一樣的。
某些必須獲得多個鎖的操作並不會像之前transfermoney中那麼明顯,這兩個鎖不一定必須在同乙個方法中獲取。
所以如果在持有某個鎖的同時,又去呼叫某個外部方法,那麼就有可能發生問題,在這個外部方法中有可能會去嘗試獲得另乙個鎖。這樣就會有發生
死鎖的風險。
所以,如果在呼叫某個方法的時候不需要持有鎖,那麼這種呼叫就被稱為開發呼叫,可以有效的避免上面這種情況發生的死鎖。
當多個執行緒相互持有彼此正在等待的鎖而又不釋放自己已持有的鎖時會發生死鎖, 當他們在相同的資源集合上等待時,也會發生死鎖
如果必須獲得多個鎖,那麼在設計時必須考慮鎖的順序:儘量減少潛在的而加鎖互動數量,將獲取鎖時需要遵循的協議寫入正式的文件並始終遵循這些協議
必要時候使用支援定時的鎖,及顯示的使用lock類中的定時trylock功能來代替內建鎖機制。當使用內建鎖時,只要沒有獲得鎖,就會永遠的等待下去,而顯示鎖則可以指定乙個超時時限,在等待了一定時間以後,如果還沒有成功獲得鎖,那麼久放棄,並返回乙個失敗資訊。
還可以通過執行緒的轉儲資訊來分析死鎖發生的資訊。
Java併發 JAVA併發程式設計實戰 讀書筆記3
發布乙個物件的意思是使它能夠被當前範圍之外的 所使用。比如將乙個引用儲存到其他 可以訪問的地方,在乙個非私有的方法中返回這個引用。在很多情況下,我們需要確保物件及它們的內部狀態不被暴露。乙個物件在尚未準備好時就將它發布,稱作逸出。最常見的發布物件的方式就是將物件的引用儲存到公共靜態域中,任何類和執行...
Java併發 JAVA併發程式設計實戰 讀書筆記8
為計算結果建立高效 可伸縮的快取記憶體 public inte ce computable public class expensivefunction implements computable public class memoizer1implements computable public ...
Java併發 JAVA併發程式設計實戰 讀書筆記14
無論何時,執行緒池需要建立乙個執行緒都要通過乙個執行緒工廠。public inte ce threadfactory 預設的執行緒工廠建立乙個新的非守護的執行緒,其中的newthread 會在建立乙個新執行緒時被呼叫。public class mythreadfactory implements t...