java如何避免死鎖
Ⅰ 死鎖避免,檢測和預防之間的區別是什麼
在有些情況下死鎖是可以避免的。本文將展示三種用於避免死鎖的技術:
加鎖順序
加鎖時限
死鎖檢測
加鎖順序
當多個線程需要相同的一些鎖,但是按照不同的順序加鎖,死鎖就很容易發生。
如果能確保所有的線程都是按照相同的順序獲得鎖,那麼死鎖就不會發生。看下面這個例子:
Thread 1:
lock A
lock B
Thread 2:
wait for A
lock C (when A locked)
Thread 3:
wait for A
wait for B
wait for C
如果一個線程(比如線程3)需要一些鎖,那麼它必須按照確定的順序獲取鎖。它只有獲得了從順序上排在前面的鎖之後,才能獲取後面的鎖。
例如,線程2和線程3隻有在獲取了鎖A之後才能嘗試獲取鎖C(譯者註:獲取鎖A是獲取鎖C的必要條件)。因為線程1已經擁有了鎖A,所以線程2和3需要一直等到鎖A被釋放。然後在它們嘗試對B或C加鎖之前,必須成功地對A加了鎖。
按照順序加鎖是一種有效的死鎖預防機制。但是,這種方式需要你事先知道所有可能會用到的鎖(譯者註:並對這些鎖做適當的排序),但總有些時候是無法預知的。
加鎖時限
另外一個可以避免死鎖的方法是在嘗試獲取鎖的時候加一個超時時間,這也就意味著在嘗試獲取鎖的過程中若超過了這個時限該線程則放棄對該鎖請求。若一個線程沒有在給定的時限內成功獲得所有需要的鎖,則會進行回退並釋放所有已經獲得的鎖,然後等待一段隨機的時間再重試。這段隨機的等待時間讓其它線程有機會嘗試獲取相同的這些鎖,並且讓該應用在沒有獲得鎖的時候可以繼續運行(譯者註:加鎖超時後可以先繼續運行干點其它事情,再回頭來重復之前加鎖的邏輯)。
以下是一個例子,展示了兩個線程以不同的順序嘗試獲取相同的兩個鎖,在發生超時後回退並重試的場景:
Thread 1 locks A
Thread 2 locks B
Thread 1 attempts to lock B but is blocked
Thread 2 attempts to lock A but is blocked
Thread 1's lock attempt on B times out
Thread 1 backs up and releases A as well
Thread 1 waits randomly (e.g. 257 millis) before retrying.
Thread 2's lock attempt on A times out
Thread 2 backs up and releases B as well
Thread 2 waits randomly (e.g. 43 millis) before retrying.
在上面的例子中,線程2比線程1早200毫秒進行重試加鎖,因此它可以先成功地獲取到兩個鎖。這時,線程1嘗試獲取鎖A並且處於等待狀態。當線程2結束時,線程1也可以順利的獲得這兩個鎖(除非線程2或者其它線程在線程1成功獲得兩個鎖之前又獲得其中的一些鎖)。
需要注意的是,由於存在鎖的超時,所以我們不能認為這種場景就一定是出現了死鎖。也可能是因為獲得了鎖的線程(導致其它線程超時)需要很長的時間去完成它的任務。
此外,如果有非常多的線程同一時間去競爭同一批資源,就算有超時和回退機制,還是可能會導致這些線程重復地嘗試但卻始終得不到鎖。如果只有兩個線程,並且重試的超時時間設定為0到500毫秒之間,這種現象可能不會發生,但是如果是10個或20個線程情況就不同了。因為這些線程等待相等的重試時間的概率就高的多(或者非常接近以至於會出現問題)。
(譯者註:超時和重試機制是為了避免在同一時間出現的競爭,但是當線程很多時,其中兩個或多個線程的超時時間一樣或者接近的可能性就會很大,因此就算出現競爭而導致超時後,由於超時時間一樣,它們又會同時開始重試,導致新一輪的競爭,帶來了新的問題。)
這種機制存在一個問題,在java中不能對synchronized同步塊設置超時時間。你需要創建一個自定義鎖,或使用Java5中java.util.concurrent包下的工具。寫一個自定義鎖類不復雜,但超出了本文的內容。後續的Java並發系列會涵蓋自定義鎖的內容。
死鎖檢測
死鎖檢測是一個更好的死鎖預防機制,它主要是針對那些不可能實現按序加鎖並且鎖超時也不可行的場景。
每當一個線程獲得了鎖,會在線程和鎖相關的數據結構中(map、graph等等)將其記下。除此之外,每當有線程請求鎖,也需要記錄在這個數據結構中。
當一個線程請求鎖失敗時,這個線程可以遍歷鎖的關系圖看看是否有死鎖發生。例如,線程A請求鎖7,但是鎖7這個時候被線程B持有,這時線程A就可以檢查一下線程B是否已經請求了線程A當前所持有的鎖。如果線程B確實有這樣的請求,那麼就是發生了死鎖(線程A擁有鎖1,請求鎖7;線程B擁有鎖7,請求鎖1)。
當然,死鎖一般要比兩個線程互相持有對方的鎖這種情況要復雜的多。線程A等待線程B,線程B等待線程C,線程C等待線程D,線程D又在等待線程A。線程A為了檢測死鎖,它需要遞進地檢測所有被B請求的鎖。從線程B所請求的鎖開始,線程A找到了線程C,然後又找到了線程D,發現線程D請求的鎖被線程A自己持有著。這是它就知道發生了死鎖。
下面是一幅關於四個線程(A,B,C和D)之間鎖佔有和請求的關系圖。像這樣的數據結構就可以被用來檢測死鎖。
那麼當檢測出死鎖時,這些線程該做些什麼呢?
一個可行的做法是釋放所有鎖,回退,並且等待一段隨機的時間後重試。這個和簡單的加鎖超時類似,不一樣的是只有死鎖已經發生了才回退,而不會是因為加鎖的請求超時了。雖然有回退和等待,但是如果有大量的線程競爭同一批鎖,它們還是會重復地死鎖(編者註:原因同超時類似,不能從根本上減輕競爭)。
一個更好的方案是給這些線程設置優先順序,讓一個(或幾個)線程回退,剩下的線程就像沒發生死鎖一樣繼續保持著它們需要的鎖。如果賦予這些線程的優先順序是固定不變的,同一批線程總是會擁有更高的優先順序。為避免這個問題,可以在死鎖發生的時候設置隨機的優先順序。
Ⅱ JAVA程序設計,多線程且避免死鎖
JAVA中幾種常見死鎖及對策:解決死鎖沒有簡單的方法,這是因為線程產生死鎖都各有各的原因,而且往往具有很高的負載。大多數軟體測試產生不了足夠多的負載,所以不可能暴露所有的線程錯誤。在這里中,下面將討論開發過程常見的4類典型的死鎖和解決對策。(1)資料庫死鎖在資料庫中,如果一個連接佔用了另一個連接所需的資料庫鎖,則它可以阻塞另一個連接。如果兩個或兩個以上的連接相互阻塞,則它們都不能繼續執行,這種情況稱為資料庫死鎖。資料庫死鎖問題不易處理,通常數據行進行更新時,需要鎖定該數據行,執行更新,然後在提交或回滾封閉事務時釋放鎖。由於資料庫平台、配置的隔離級以及查詢提示的不同,獲取的鎖可能是細粒度或粗粒度的,它會阻塞(或不阻塞)其他對同一數據行、表或資料庫的查詢。基於資料庫模式,讀寫操作會要求遍歷或更新多個索引、驗證約束、執行觸發器等。每個要求都會引入鎖。此外,其他應用程序還可能正在訪問同一資料庫模式中的某些對象,並獲取不同應用程序所具有的鎖。所有這些因素綜合在一起,資料庫死鎖幾乎不可能被消除了。值得慶幸的是,資料庫死鎖通常是可恢復的:當資料庫發現死鎖時,它會強制銷毀一個連接(通常是使用最少的連接),並回滾其事務。這將釋放所有與已經結束的事務相關聯的鎖,至少允許其他連接中有一個可以獲取它們正在被阻塞的鎖。由於資料庫具有這種典型的死鎖處理行為,所以當出現資料庫死鎖問題時,資料庫常常只能重試整個事務。當資料庫連接被銷毀時,會拋出可被應用程序捕獲的異常,並標識為資料庫死鎖。如果允許死鎖異常傳播到初始化該事務的代碼層之外,則該代碼層可以啟動一個新事務並重做先前所有工作。當出現問題就重試,由於資料庫可以自由地獲取鎖,所以幾乎不可能保證兩個或兩個以上的線程不發生資料庫死鎖。此方法至少能保證在出現某些資料庫死鎖情況時,應用程序能正常運行。(2)資源池耗盡死鎖客戶端的增加導致資源池耗盡死鎖是由於負載而造成的,即資源池太小,而每個線程需要的資源超過了池中的可用資源。假設連接池最多有10個連接,同時有10個對外部並發調用。這些線程中每一個都需要一個資料庫連接用來清空池。現在,每個線程都執行嵌套的調用。則所有線程都不能繼續,但又都不放棄自己的第一個資料庫連接。這樣,10個線程都將被死鎖。研究此類死鎖,會發現線程存儲中有大量等待獲取資源的線程,以及同等數量的空閑且未阻塞的活動資料庫連接。當應用程序死鎖時,如果可以在運行時檢測連接池,就能確認連接池實際上已空。修復此類死鎖的方法包括:增加連接池的大小或者重構代碼,以便單個線程不需要同時使用很多資料庫連接。或者可以設置內部調用使用不同的連接池,即使外部調用的連接池為空,內部調用也能使用自己的連接池繼續。(3)單線程、多沖突資料庫連接死鎖對同一線程執行嵌套的調用有時出現死鎖,此情形即使在非高負載系統中通常也會發生。當第一個(外部)連接已獲取第二個(內部)連接所需要的資料庫鎖,則第二個連接將永久阻塞第一個連接,並等待第一個連接被提交或回滾,這就出現了死鎖情形。因為資料庫沒有注意到兩個連接之間的關系,所以資料庫不會將此情形檢測為死鎖。這樣即使不存在並發,此代碼也將導致死鎖。此情形有多種具體的變種,可以涉及多個線程和兩個以上的資料庫連接。(4)Java虛擬機鎖與資料庫鎖沖突這種情形發生在資料庫鎖與Java虛擬機鎖並存的時候。在這種情況下,一個線程佔有一個資料庫鎖並嘗試獲取Java虛擬機鎖。同時,另一個線程佔有Java虛擬機鎖並嘗試獲取資料庫鎖。此時,資料庫發現一個連接阻塞了另一個連接,但由於無法阻止連接繼續,所以不會檢測到死鎖。Java虛擬機發現同步的鎖中有一個線程,並有另一個嘗試進入的線程,所以即使Java虛擬機能檢測到死鎖並對它們進行處理,它還是不會檢測到這種情況。總而言之,JAVA應用程序中的死鎖是一個大問題——它能導致整個應用程序慢慢終止,還很難被分離和修復,尤其是當開發人員不熟悉如何分析死鎖環境的時候。五.死鎖的經驗法則筆者在開發中總結以下死鎖問題的經驗。(1)對大多數的Java程序員來說最簡單的防止死鎖的方法是對競爭的資源引入序號,如果一個線程需要幾個資源,那麼它必須先得到小序號的資源,再申請大序號的資源。可以在Java代碼中增加同步關鍵字的使用,這樣可以減少死鎖,但這樣做也會影響性能。如果負載過重,資料庫內部也有可能發生死鎖。(2)了解資料庫鎖的發生行為。假定任何資料庫訪問都有可能陷入資料庫死鎖狀況,但是都能正確進行重試。例如了解如何從應用伺服器獲取完整的線程轉儲以及從資料庫獲取資料庫連接列表(包括互相阻塞的連接),知道每個資料庫連接與哪個Java線程相關聯。了解Java線程和資料庫連接之間映射的最簡單方法是向連接池訪問模式添加日誌記錄功能。(3)當進行嵌套的調用時,了解哪些調用使用了與其它調用同樣的資料庫連接。即使嵌套調用運行在同一個全局事務中,它仍將使用不同的資料庫連接,而不會導致嵌套死鎖。(4)確保在峰值並發時有足夠大的資源池。(5)避免執行資料庫調用或在佔有Java虛擬機鎖時,執行其他與Java虛擬機無關的操作。最重要的是,多線程設計雖然是困難的,但在開始編程之前詳細設計系統能夠幫助你避免難以發現死鎖的問題。死鎖在語言層面上不能解決,就需要一個良好設計來避免死鎖。
Ⅲ 北大青鳥java培訓:在Java程序中處理資料庫超時與死鎖
每個使用關系型資料庫的程序都可能遇到數據死鎖或不可用的情況,而這些情況需要在代碼中編程來解決;本文主要介紹與資料庫事務死鎖等情況相關的重試邏輯概念,此外,還會探討如何避免死鎖等問題,文章以DB2(版本9)與為例進行講解。
什麼是資料庫鎖定與死鎖鎖定(Locking)發生在當一個事務獲得對某一資源的「鎖」時,這時,其他的事務就不能更改這個資源了,這種機制的存在是為了保證數據一致性;在設計與資料庫交互的程序時,必須處理鎖與資源不可用的情況。
鎖定是個比較復雜的概念,仔細說起來可能又需要一大篇,所以在本文中,只把鎖定看作是一個臨時事件,這意味著如果一個資源被鎖定,它總會在以後某個時間被釋放。
而死鎖發生在當多個進程訪問同一資料庫時,其中每個進程擁有的鎖都是其他進程所需的,由此造成每個進程都無法繼續下去。
如何避免鎖我們可利用事務型資料庫中的隔離級別機制來避免鎖的創建,正確地使用隔離級別可使程序處理更多的並發事件(如允許多個用戶訪問數據),還能預防像丟失修改(LostUpdate)、讀「臟」數據(DirtyRead)、不可重復讀(NonrepeatableRead)及「虛」(Phantom)等問題。
隔離級別問題現象丟失修改讀「臟」數據不可重復讀「虛」可重復讀取NoNoNoNo讀取穩定性NoNoNoYes游標穩定性NoNoYesYes未提交的讀NoYesYesYes表1:DB2的隔離級別與其對應的問題現象在只讀模式中,就可以防止鎖定發生,而不用那些未提交只讀隔離級別的含糊語句。
湖北電腦培訓http://www.kmbdqn.cn/發現一條SQL語句當使用了下列命令之一時,就應該考慮只讀模式了
Ⅳ java的死鎖是什麼,如何避免死鎖
我們先看看這樣一個生活中的例子:在一條河上有一座橋,橋面較窄,只能容納一輛汽車通過,無法讓兩輛汽車並行。如果有兩輛汽車A和B分別由橋的兩端駛上該橋,則對於A車來說,它走過橋面左面的一段路(即佔有了橋的一部分資源),要想過橋還須等待B車讓出右邊的橋面,此時A車不能前進;對於B車來說,它走過橋面右邊的一段路(即佔有了橋的一部分資源),要想過橋還須等待A車讓出左邊的橋面,此時B車也不能前進。兩邊的車都不倒車,結果造成互相等待對方讓出橋面,但是誰也不讓路,就會無休止地等下去。這種現象就是死鎖。如果把汽車比做進程,橋面作為資源,那麽上述問題就描述為:進程A佔有資源R1,等待進程B佔有的資源Rr;進程B佔有資源Rr,等待進程A佔有的資源R1。而且資源R1和Rr只允許一個進程佔用,即:不允許兩個進程同時佔用。結果,兩個進程都不能繼續執行,若不採取其它措施,這種循環等待狀況會無限期持續下去,就發生了進程死鎖。
在計算機系統中,涉及軟體,硬體資源都可能發生死鎖。例如:系統中只有一台CD-ROM驅動器和一台列印機,某一個進程佔有了CD-ROM驅動器,又申請列印機;另一進程佔有了列印機,還申請CD-ROM。結果,兩個進程都被阻塞,永遠也不能自行解除。
所謂死鎖,是指多個進程循環等待它方佔有的資源而無限期地僵持下去的局面。很顯然,如果沒有外力的作用,那麽死鎖涉及到的各個進程都將永遠處於封鎖狀態。從上面的例子可以看出,計算機系統產生死鎖的根本原因就是資源有限且操作不當。即:一種原因是系統提供的資源太少了,遠不能滿足並發進程對資源的需求。這種競爭資源引起的死鎖是我們要討論的核心。例如:消息是一種臨時性資源。某一時刻,進程A等待進程B發來的消息,進程B等待進程C發來的消息,而進程C又等待進程A發來的消息。消息未到,A,B,C三個進程均無法向前推進,也會發生進程通信上的死鎖。另一種原因是由於進程推進順序不合適引發的死鎖。資源少也未必一定產生死鎖。就如同兩個人過獨木橋,如果兩個人都要先過,在獨木橋上僵持不肯後退,必然會應競爭資源產生死鎖;但是,如果兩個人上橋前先看一看有無對方的人在橋上,當無對方的人在橋上時自己才上橋,那麽問題就解決了。所以,如果程序設計得不合理,造成進程推進的順序不當,也會出現死鎖。
Ⅳ 鍦↗ava紼嬪簭璁捐′腑濡備綍閬垮厤姝婚攣
1銆佸湪紼嬪簭涓灝介噺浣跨敤寮鏀捐皟鐢ㄣ備緷璧栦簬寮鏀捐皟鐢ㄧ殑紼嬪簭錛岀浉姣斾簬閭d簺鍦ㄦ寔鏈夐攣鐨勬椂鍊欒繕璋冪敤澶栭儴鏂規硶鐨勭▼搴忥紝鏇村規槗榪涜屾婚攣鑷鐢卞害鐨勫垎鏋愩傞噸鏂版瀯寤簊ynchronized浣垮紑鏀捐皟鐢ㄦ洿鍔犲畨鍏ㄣ傛墍璋撳紑鏀捐皟鐢ㄦ槸鎸囪皟鐢ㄧ殑鏂規硶鏈韜娌℃湁鍔犻攣錛屼絾鏄瑕佷互瀵規柟娉曟搷浣滅殑鍐呭硅繘琛屽姞閿併
2銆佸傛灉浣犲繀欏昏幏寰楀氫釜閿侊紝閭d箞閿佺殑欏哄簭蹇呴』鏄浣犺捐″伐浣滅殑涓閮ㄥ垎錛氬敖閲忓噺灝戞綔鍦ㄩ攣涔嬮棿鐨勪氦浜掓暟閲忥紝閬靛畧騫舵枃妗e寲璇ラ攣欏哄簭鍗忚銆傜洃嫻嬩唬鐮佷腑姝婚攣鑷鐢卞害鐨勭瓥鐣ユ湁錛
1錛夎瘑鍒浠涔堝湴鏂逛細鑾峰彇澶氫釜閿侊紝騫朵嬌閿佹暟閲忓敖鍙鑳藉皯錛屼繚璇佸畠浠鐨勯『搴忓湪紼嬪簭涓涓鑷淬
2錛夊湪娌℃湁闈炲紑鏀捐皟鐢ㄧ殑紼嬪簭涓錛屽彂鐜伴偅浜涜幏寰楀氶噸閿佺殑瀹炰緥鏄闈炲父綆鍗曠殑銆
3銆佸皾璇曞畾鏃剁殑閿侊紝浣跨敤姣忎釜鏄懼紡Lock綾諱腑瀹氭椂tryLock鐗規э紝鏉ユ浛浠d嬌鐢ㄥ唴閮ㄩ攣鏈哄埗