java中的死鎖問題研究

1.什麼是死鎖:

百度百科的定義:java

所謂死鎖:是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。dom

java中死鎖:某個任務在等待另外一個任務釋放鎖,然後者又等待別的任務釋放鎖,這樣一直下去,直到這個鏈上的任務又在等待第一個任務釋放鎖,這獲得了一個任務之間的相互等待的連續循環,沒有哪一個線程能繼續,這被稱之爲死鎖。ide

2.經典的死鎖現象---哲學家就餐問題

     由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上產生競爭,而死鎖也就會更快的發生。

3.預防死鎖

死鎖必備條件

必須當一下四個條件同時知足,就會發生死鎖:

  • 1) 互斥條件。
  • 2) 至少有一個任務他必須持有一個資源且正在等待獲取一個當前被別的任務持有的資源。   也就是說,要發生死鎖,Philosopher必須拿着一個筷子,而且等待另一根。
  • 3) 資源不能被任務搶佔。
  • 4) 必須有循環等待。這時,一個任務等待其餘任務所持有的資源,後者又在等待另一個任務所持有的資源,這樣一直下去,直到有一個任務在等待第一個任務所持有的資源,使得你們都被鎖住。在咱們的例子中,每一個Philosopher都試圖先獲得右邊的Chopstick,而後獲得左邊的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,咱們能夠移除死鎖,從而使這個程序平滑的運行。

相關文章
相關標籤/搜索