線程死鎖
死鎖是兩個或更多線程阻塞著等待其它處于死鎖狀態(tài)的線程所持有的鎖胧辽。死鎖通常發(fā)生在多個線程同時但以不同的順序請求同一組鎖的時候糙臼。
public class TreeNode {
TreeNode parent = null;
List children = new ArrayList();
public synchronized void addChild(TreeNode child){
if(!this.children.contains(child)) {
this.children.add(child);
child.setParentOnly(this);
}
}
public synchronized void addChildOnly(TreeNode child){
if(!this.children.contains(child){
this.children.add(child);
}
}
public synchronized void setParent(TreeNode parent){
this.parent = parent;
parent.addChildOnly(this);
}
public synchronized void setParentOnly(TreeNode parent){
this.parent = parent;
}
}
// Thread 1: parent.addChild(child); //locks parent
--> child.setParentOnly(parent);
// Thread 2: child.setParent(parent); //locks child
--> parent.addChildOnly()
如果child和parent是同一個對象,而且兩個線程同時執(zhí)行,兩個線程同時獲得鎖,那么此時就會發(fā)生死鎖愉适。沒有辦法預測什么時候死鎖會發(fā)生,僅僅是可能會發(fā)生癣漆。
數(shù)據(jù)庫死鎖
當在一個事務中更新一條記錄维咸,這條記錄就會被鎖住避免其他事務的更新請求,直到第一個事務結束惠爽。同一個事務中每一個更新請求都可能會鎖住一些記錄癌蓖。當多個事務同時需要對一些相同的記錄做更新操作時,就很有可能發(fā)生死鎖婚肆。
死鎖的預防
鎖的順序
死鎖在多線程以不同的順序請求相同的鎖的時候發(fā)生租副。如果保證所有線程加鎖的順序相同,那么久不會發(fā)生死鎖较性。
public class DeadLockFixed {
public void method1() {
synchronized(Integer.class) {
System.out.println("Aquired lock on Integer.class object");
synchronized (String.class) {
System.out.println("Aquired lock on String.class object");
}
}
}
public void method2() {
synchronized (Integer.class) {
System.out.println("Aquired lock on Integer.class object");
synchronized (String.class) {
System.out.println("Aquired lock on String.class object");
}
}
}
}
鎖超時
另外一個可以避免死鎖的方法是在嘗試獲取鎖的時候加一個超時時間用僧,這也就意味著在嘗試獲取鎖的過程中若超過了這個時限該線程則放棄對該鎖請求。若一個線程沒有在給定的時限內成功獲得所有需要的鎖赞咙,則會進行回退并釋放所有已經(jīng)獲得的鎖责循,然后等待一段隨機的時間再重試。這段隨機的等待時間讓其它線程有機會嘗試獲取相同的這些鎖攀操,并且讓該應用在沒有獲得鎖的時候可以繼續(xù)運行院仿。
如果有非常多的線程同一時間去競爭同一批資源,就算有超時和回退機制崔赌,還是可能會導致這些線程重復地嘗試但卻始終得不到鎖意蛀。如果只有兩個線程,并且重試的超時時間設定為0到500毫秒之間健芭,這種現(xiàn)象可能不會發(fā)生县钥,但是如果是10個或20個線程情況就不同了。
死鎖檢測
使用一個數(shù)據(jù)結構維護線程所獲取的鎖慈迈。當鎖請求失敗的時候若贮,線程遍歷鎖關系圖查看是否有死鎖發(fā)生。
解決死鎖方法:
- 釋放所有鎖痒留,回退谴麦,等待一定時間后重試
- 給這些線程設置優(yōu)先級,讓一個或者多個線程進行回退