百度百科的定义: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,咱们能够移除死锁,从而使这个程序平滑的运行。