1. 程式人生 > >Java併發程式設計(9):死鎖(含程式碼)

Java併發程式設計(9):死鎖(含程式碼)

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、建立和使用一個大鎖來代替若干小鎖,並把這個鎖用於互斥,而不是用作單個物件的物件級別鎖;