您的位置:首页 > 编程语言 > Java开发

Java 线程与并发研究系列六(死锁)

2014-07-22 01:16 525 查看
我们在学习操作系统的时候,一定接触过哲学家就餐的问题,这是由Edsger Dijkstra提出的经典的死锁例子,该例子描述的是,指定

五个哲学家一起就餐,这些哲学家花一部分时间就餐,一部分时间思考,就餐时,总共五把叉子,每人一把,如果需要就餐就必须拿

到左边或右边的叉子,直到手中有两把叉子时才能就餐,否则哲学家就必须等待,直到得到必须的叉子。

下面我们就编写程序来实现这样一个场景

public class Test {

public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
Fork [] array = new Fork[5];
for (int i = 0; i < 5; i++) {
array[i] = new Fork();
}

for (int i = 0; i < array.length; i++) {
exec.execute(new Philosopher(array[i], array[(i+1)%5],5,i));
}
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
exec.shutdownNow();
}
}

class Philosopher implements Runnable{
Fork forkLeft,forkRight;
int time;//休息时间
int num;//哲学家编号
Random rand = new Random();

public Philosopher(Fork forkLeft,Fork forkRight,int time,int num){
this.forkLeft = forkLeft;
this.forkRight = forkRight;
this.time = time;
this.num = num;
}

@Override
public void run() {
try {
while(!Thread.interrupted()){
forkLeft.take();
forkRight.take();

rest();

forkLeft.drop();
forkRight.drop();
System.out.println("哲学家"+num+"号已经吃了一次");
}
} catch (InterruptedException e) {
System.out.println("结束用餐");
}
}

public void rest(){
try {
TimeUnit.MILLISECONDS.sleep(rand.nextInt(time*250));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class Fork{

private boolean isTake = false;

public synchronized void take()throws InterruptedException{
while(isTake){
wait();
}
isTake = true;
}

public synchronized void drop()throws InterruptedException{
isTake = false;
notifyAll();
}
}


上面Fork类表示叉子,Philosopher表示哲学家,哲学家分别从左边和右边的人那里利用take方法获取叉子,吃完后在调用drop方法进行释放。

上面的例子,我只让他运行了十秒,在这个时间段你可能看不到死锁,如果你永久的运行下面可能就会产生死锁。为什么会产生死锁呢?从

上面的例子中我们可以看到,哲学家在用餐的时候都是先获取左边的叉子,然后获取右边的叉子,如果获取到了左边的叉子,在获取右边的叉子

时,右边的叉子已经被人获取了,那么他就处于等待状态,同时没有释放之前获取的左边的叉子。假如他们都处于这样一个场景:0号哲学家获取

了左边4号哲学家的叉子,1号获取0号的,2号获取1号的,3号获取2号的,4号获取3号的,只要遇到这样的场景,那么就永远不会有人同时拿到

2把叉子,所有的人都会永久的等待下去,这样就造成了死锁。

造成死锁的几个必要条件:

1、互斥条件,至少有一个条件是不能共享的,这里的叉子就不能同时被两个人使用。

2、循环等待,一个任务等待另一个任务所持有的资源,而另一个任务又在等待前者所持有的资源。这里如果哲学家都相互持有另外一个哲学家的

叉子后,就会相互等待另一方释放叉子,那么就会造成循环等待的场景。

3、非抢占,任何任务所持有的资源,除非自己释放掉,否则其他任务不能抢占。

4、持有等待,至少有一个任务必须持有一个资源,并且正在等待获取一个当前被其他任务持有的资源。这里哲学家获取到了左边的人的叉子,如果

右边的人叉子已经被其他的人获取,那么这个哲学家就会持有左边的人的叉子,并且等待其他人释放右边的人的叉子。

那么怎样来预防上面这个可能会产生死锁的例子呢?只要破坏上面4个条件中的一个就会打破死锁的局面,在这里我们打破第二个条件就会消除死锁

只要其中有一人先获取右边再获取左边的叉子就会打破这种局面,这样就会使得桌子上至少能够存在一对叉子可以使用,这一死锁便解除了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: