百度百科的定義:java
所謂死鎖:是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。dom
java中死鎖:某個任務在等待另外一個任務釋放鎖,然後者又等待別的任務釋放鎖,這樣一直下去,直到這個鏈上的任務又在等待第一個任務釋放鎖,這獲得了一個任務之間的相互等待的連續循環,沒有哪一個線程能繼續,這被稱之爲死鎖。ide
由Edsger Dijkstra提出的哲學家就餐問題是一個經典的死鎖例證。測試
哲學家就餐問題描述:ui
設有五位哲學家圍坐在一張圓形餐桌旁,作如下兩件事情之一:吃飯,或者思考。吃東西的時候,他們就中止思考,思考的時候也中止吃東西。餐桌中間不少食物,每兩個哲學家之間有一隻筷子。當一個哲學家要就餐的時候,這個哲學家必須同時獲得左邊和右邊的筷子,若是一個哲學家左邊或右邊已經有人在使用筷子,那麼這個哲學家就必須等待,直到獲得必需的筷子。this
下面就是哲學家就餐問題的java編寫的程序:spa
a.筷子類線程
public class Chopstick { private boolean taken = false; public synchronized void take() throws InterruptedException { while(taken) wait(); taken = true; } public synchronized void drop() { taken = false; notifyAll(); } } ///:~
任何兩個哲學家都不能take()同一個筷子,另外,若是一根筷子已經被某個哲學家得到,那麼另一個哲學家就要wait(),直至這個筷子的當前持有者調用drop()使其可使用爲止。code
b.哲學家的類進程
import java.util.concurrent.*; import java.util.*; import static mutex.conflict.Print.*; public class Philosopher implements Runnable { private Chopstick left; private Chopstick right; private final int id; private final int ponderFactor; private Random rand = new Random(47); private void pause() throws InterruptedException { if(ponderFactor == 0) return; TimeUnit.MILLISECONDS.sleep( rand.nextInt(ponderFactor * 250)); } public Philosopher(Chopstick left, Chopstick right, int ident, int ponder) { this.left = left; this.right = right; id = ident; ponderFactor = ponder; } public void run() { try { while(!Thread.interrupted()) { print(this + " " + "thinking"); pause(); // Philosopher becomes hungry print(this + " " + "grabbing right"); right.take(); print(this + " " + "grabbing left"); left.take(); print(this + " " + "eating"); pause(); right.drop(); left.drop(); } } catch(InterruptedException e) { print(this + " " + "exiting via interrupt"); } } public String toString() { return "Philosopher " + id; } }
在哲學家的run()中,每一個Philosopher只是不斷的思考和吃飯。若是PonderFactor不爲0,則pause()方法會休眠一段隨機時間。經過這種方式,你將看到Philosopher會在思考上花掉一段隨機化的時間,而後嘗試或者獲取(take())右邊和左邊的筷子,隨後在吃飯再花掉隨機化的時間,以後重複此過程。
c.測試程序,會發生死鎖:
import java.util.concurrent.*; public class DeadlockingDiningPhilosophers { public static void main(String[] args) throws Exception { int ponder = 1; if (args.length > 0) ponder = Integer.parseInt(args[0]); int size = 5; if (args.length > 1) size = Integer.parseInt(args[1]); ExecutorService exec = Executors.newCachedThreadPool(); Chopstick[] sticks = new Chopstick[size]; for (int i = 0; i < size; i++) sticks[i] = new Chopstick(); for (int i = 0; i < size; i++) exec.execute(new Philosopher(sticks[i], sticks[(i + 1) % size], i, ponder)); if (args.length == 3 && args[2].equals("timeout")) TimeUnit.SECONDS.sleep(5); else { System.out.println("Press 'Enter' to quit"); System.in.read(); } exec.shutdownNow(); } }
你會發現,若是Philosopher花在思考上的時間很是少,那麼當他們想要進餐時,全都會在Chopstick上產生競爭,而死鎖也就會更快的發生。
死鎖必備條件
必須當一下四個條件同時知足,就會發生死鎖:
因此要解決死鎖,只要破壞其中一個條件便可,在這個程序中,最容易的方法是破壞第四個條件。有這個條件的緣由是每一個Philosopher都試圖用特定的順序拿Chopstick:先右後左,正因如此,就可能會發生"每一個人都拿着右邊的Chopstick,並等待左邊的Chopstick"的狀況。這就是循環等待條件,而後若是最後一個Philosopher被初始化成先拿左邊的Chopstick,在本例中,這就能夠防止循環等待。這只是問題的解決辦法之一,固然也能夠經過破壞其餘條件來防止死鎖。
最後防止死鎖的方法代碼以下:
import java.util.concurrent.*; public class FixedDiningPhilosophers { public static void main(String[] args) throws Exception { int ponder = 5; if(args.length > 0) ponder = Integer.parseInt(args[0]); int size = 5; if(args.length > 1) size = Integer.parseInt(args[1]); ExecutorService exec = Executors.newCachedThreadPool(); Chopstick[] sticks = new Chopstick[size]; for(int i = 0; i < size; i++) sticks[i] = new Chopstick(); for(int i = 0; i < size; i++) if(i < (size-1)) exec.execute(new Philosopher( sticks[i], sticks[i+1], i, ponder)); else exec.execute(new Philosopher( sticks[0], sticks[i], i, ponder)); if(args.length == 3 && args[2].equals("timeout")) TimeUnit.SECONDS.sleep(5); else { System.out.println("Press 'Enter' to quit"); System.in.read(); } exec.shutdownNow(); } }
經過確保最後一個Philosopher先拿起和放下左邊的Chopstick,咱們能夠移除死鎖,從而使這個程序平滑的運行。