鎖活躍性問題(死鎖,飢餓、活鎖)
一、死鎖
每個人都擁有其他人需要的資源,同時又等待其他人已經擁有的資源,並且每個人在獲取所有需要的資源之前都不會放棄已經擁有的資源。
當一個執行緒永遠地持有一個鎖,並且其他執行緒都嘗試去獲得這個鎖時,那麼它們將永遠被阻塞,這個我們都知道。如果執行緒A持有鎖L並且想獲得鎖M,執行緒B持有鎖M並且想獲得鎖L,那麼這兩個執行緒將永遠等待下去,這種情況就是最簡單的死鎖形式。其中多個執行緒因為存在環路鎖依賴關係而永遠地等待下去。
在資料庫系統的設計中考慮了監測死鎖以及從死鎖中恢復,資料庫如果監測到了一組事物發生了死鎖時,將選擇一個犧牲者並放棄這個事物。Java虛擬機器解決死鎖問題方面並沒有資料庫這麼強大,當一組Java執行緒發生死鎖時,這兩個執行緒就永遠不能再使用了,並且由於兩個執行緒分別持有了兩個鎖,那麼這兩段同步程式碼/程式碼塊也無法再運行了----除非終止並重啟應用。
1、判斷死鎖總結:把每個執行緒假象成有向圖中的一個節點,圖中每條邊的關係是:“執行緒A等待執行緒B所佔有的資源”。如果在圖中形成了一條環路,那麼久存在一個死鎖。
2、死鎖的必要條件:
a、互斥條件:一個資源每次只能被一個程序使用
b、請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。
c、不剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪。
d、迴圈等待條件:若干程序之間形成一種頭尾相接的迴圈等待資源關係。
3、死鎖的幾種常見常見
(1)、鎖順序死鎖(LeftRightDeadLock):兩個執行緒以不同的順序 來獲得相同的鎖。
public class LeftRightDeadLock { private final Object left = new Object(); private final Object right = new Object(); public void leftRight() throws Exception { synchronized (left) { Thread.sleep(5000); synchronized (right) { System.out.println("leftRight end!"); } } } public void rightLeft() throws Exception { synchronized (right) { Thread.sleep(5000); synchronized (left) { System.out.println("rightLeft end!"); } } } }
如果所有執行緒以固定的順序來獲取鎖,就不會出現死鎖。
如LeftRightDeadLock每個執行緒都需要獲取left和right兩個鎖,如果都是按照left、right或者right、left的順序獲得鎖就不會發生死鎖,一旦一個執行緒按照leftright順序而另外一個rightleft順序就有可能發生死鎖
(2)、動態的鎖順序死鎖(transferMoney)
public static void transferMoney(Account fromAccount,Account toAccount,long amount){ synchronized (fromAccount){ synchronized (toAccount){ fromAccount.debit(amount); toAccount.credit(amount); } } }
A:transferMoney(myaccount,youraccount,10)
B:transferMoney(youraccount,myaccount,20)
如果執行時序不當,A可能獲得myaccount鎖並等待youraccount鎖,而B可能獲得youraccount鎖而等待myaccount鎖
(3)、在協作物件之間發生的死鎖
如果在持有鎖時呼叫某個外部方法,那麼可能產生死鎖,因為在這個外部方法中可能會獲取其他的鎖或者阻塞時間過長,導致其他執行緒無法及時獲得當前被持有的鎖。
因此,應該避免鎖所作用的範圍內呼叫外部的方法。
(4)、開發呼叫
減少鎖的範圍,把不需要加鎖的程式碼移除鎖的範圍域,比如,把使用synchronized同步的方法,改成使用synchronized同步部分程式碼塊。
(5)、資源死鎖
二、飢餓
當執行緒由於無法訪問它所需要的資源而不能繼續執行時,就發生了“飢餓”。
例如:如果java應用程式中對執行緒的優先順序使用不當,或者在持有鎖時執行一些無法結束的結構(死迴圈或者無限制地等待某個資源),那麼可能導致飢餓,因為其他需要這個鎖的執行緒將無法得到它。
總結:要避免使用執行緒的優先順序,因為這會增加平臺的依賴性,並導致死鎖。在大多數的併發程式中都使用預設的執行緒優先順序。
三、活鎖
該問題不會阻塞執行緒,但是也不能繼續執行,因為執行緒將不斷重複執行相同的操作,而且總是失敗。
要解決這種活鎖問題,需要在失敗重試機制中引入隨機性。