1. 程式人生 > >Java並發編程(9):死鎖(含代碼)

Java並發編程(9):死鎖(含代碼)

卻又 產生 因此 break 都沒有 run new t 結果 架構

JAVA大數據中高級架構 2018-11-10 14:04:32
當線程需要同時持有多個鎖時,有可能產生死鎖。考慮如下情形:

線程A當前持有互斥所鎖lock1,線程B當前持有互斥鎖lock2。接下來,當線程A仍然持有lock1時,它試圖獲取lock2,因為線程B正持有lock2,因此線程A會阻塞等待線程B對lock2的釋放。如果此時線程B在持有lock2的時候,也在試圖獲取lock1,因為線程A正持有lock1,因此線程B會阻塞等待A對lock1的釋放。二者都在等待對方所持有鎖的釋放,而二者卻又都沒釋放自己所持有的鎖,這時二者便會一直阻塞下去。這種情形稱為死鎖。

下面給出一個兩個線程間產生死鎖的示例,如下:

public class Deadlock extends Object {
private String objID;

public Deadlock(String id) {
objID = id;
}

public synchronized void checkOther(Deadlock other) {
print("entering checkOther()");
try { Thread.sleep(2000); }
catch ( InterruptedException x ) { }
print("in checkOther() - about to " + "invoke ‘other.action()‘");

//調用other對象的action方法,由於該方法是同步方法,因此會試圖獲取other對象的對象鎖
other.action();
print("leaving checkOther()");
}

public synchronized void action() {
print("entering action()");
try { Thread.sleep(500); }
catch ( InterruptedException x ) { }
print("leaving action()");
}

public void print(String msg) {

threadPrint("objID=" + objID + " - " + msg);
}

public static void threadPrint(String msg) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + ": " + msg);
}

public static void main(String[] args) {
final Deadlock obj1 = new Deadlock("obj1");
final Deadlock obj2 = new Deadlock("obj2");

Runnable runA = new Runnable() {
public void run() {
obj1.checkOther(obj2);
}
};

Thread threadA = new Thread(runA, "threadA");
threadA.start();

try { Thread.sleep(200); }
catch ( InterruptedException x ) { }

Runnable runB = new Runnable() {
public void run() {
obj2.checkOther(obj1);
}
};

Thread threadB = new Thread(runB, "threadB");
threadB.start();

try { Thread.sleep(5000); }
catch ( InterruptedException x ) { }

threadPrint("finished sleeping");

threadPrint("about to interrupt() threadA");
threadA.interrupt();

try { Thread.sleep(1000); }
catch ( InterruptedException x ) { }

threadPrint("about to interrupt() threadB");
threadB.interrupt();

try { Thread.sleep(1000); }
catch ( InterruptedException x ) { }

threadPrint("did that break the deadlock?");
}
}
運行結果如下:

從結果中可以看出,在執行到other.action()時,由於兩個線程都在試圖獲取對方的鎖,但對方都沒有釋放自己的鎖,因而便產生了死鎖,在主線程中試圖中斷兩個線程,但都無果。

大部分代碼並不容易產生死鎖,死鎖可能在代碼中隱藏相當長的時間,等待不常見的條件地發生,但即使是很小的概率,一旦發生,便可能造成毀滅性的破壞。避免死鎖是一件困難的事,遵循以下原則有助於規避死鎖:

1、只在必要的最短時間內持有鎖,考慮使用同步語句塊代替整個同步方法;

2、盡量編寫不在同一時刻需要持有多個鎖的代碼,如果不可避免,則確保線程持有第二個鎖的時間盡量短暫;

3、創建和使用一個大鎖來代替若幹小鎖,並把這個鎖用於互斥,而不是用作單個對象的對象級別鎖;

Java並發編程(9):死鎖(含代碼)