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

java多线程--哲学家就餐问题

2015-11-26 14:53 525 查看
在使用java中的sychronized或者显示锁来进行互斥操作时,就可能会出现死锁的情况:即任务一获得A资源,等待B资源。任务二获得B资源等待C资源。任务三获得C资源等待D资源。而任务四获得D资源等待A资源,这样就造成一个连续的循环等待,没有哪个线程能够继续下去,称为死锁。

对于死锁,哲学家就餐问题就是一个经典的例子。

问题描述[1]:

在一个圆桌上,有n个哲学家,n只筷子,每个哲学家左右两边各返一只筷子。哲学家可以进行思考和吃饭,思考时,不获取筷子。吃饭时,必须同时获得左右两只筷子才能吃饭(先获得右边,再获得左边)。

先来看看chopstick类:

package philosopher;
/**
* 满足:
* 每根筷子同时只能被一个哲学家获得,若有另外一个哲学家请求获得该筷子,则需要等待
* 哲学家使用完筷子之后就放回并通知其他哲学家使用
* @author lecky
*
*/
public class Chopstick {
private int index;
private boolean use = false;

public Chopstick(int index) {
super();
this.index = index;
}

@Override
public String toString() {
return "Chopstick [index=" + index + "]";
}

/*
* 获得筷子
* 该筷子被获得之后,当有其他哲学家线程来请求获得时,都需要等待
*/
public synchronized void take() throws InterruptedException{
while(use)
wait();
use =true;
}

/*
* 归还筷子
* 当持有该筷子的哲学家使用完毕之后,就将其归还,并通知其他在等待该筷子资源的哲学家
*/
public synchronized void drop(){
use = false;
notifyAll();
}
}


Philosopher类:

哲学家先获得右边的筷子,再获得左边的筷子。在获得左边的筷子时,若左边的筷子已经被相邻哲学家获得,则需要等待直到其释放该资源为止,即left.take()会阻塞,直到被通知。

package philosopher;

import java.util.Random;

/**
* 每个哲学家可以进行思考或者吃饭,吃饭时需要先后获得右边和左边的筷子
* 若没有同时获得右边和左边的筷子,则等待,
* 若使用完之后就返回。
* @author lecky
*
*/
public class Philosopher implements Runnable{
private Chopstick right ;
private Chopstick left;
private int index;
private int thinkTime;
public Philosopher(Chopstick right, Chopstick left, int index, int thinkingTime) {
super();
this.right = right;
this.left = left;
this.index = index;
this.thinkTime = thinkingTime;
}

@Override
public void run() {
try {
while (!Thread.interrupted()) {
System.out.println(this + " thinking .......");
thinking();
System.out.println(this+" start to eat and take right stick");
right.take();
System.out.println(this+" take left stick");
left.take();
System.out.println(this+" eating");
thinking();//吃饭
right.drop();
left.drop();
}
} catch (InterruptedException e) {
System.out.println(this+"InterruptedException");
}

}

/**
* 哲学家思考时间,由thinkingTime因子决定
* @throws InterruptedException
*/
private void thinking() throws InterruptedException{
Thread.sleep(thinkTime*100);
}

@Override
public String toString() {
return "Philosopher [index=" + index + "]";
}
}


产生死锁的版本:

测试类中有5个哲学家和5只筷子,将思考时间定为0,就会很快出现死锁的情况。

package philosopher;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Test;

/**
* 会产生死锁的版本
* 5个哲学家,5只筷子,每个哲学家吃饭之前需要先拿到右边的筷子,然后再拿到左边的筷子
* 之后才能吃饭
* @author lecky
*
*/
public class DeadlockPhilosopher {

@Test
public void test() throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
int size=5;
int thinkingTime=0;
Chopstick[] chopstick = new Chopstick[size];
for(int i=0;i<size;i++)
chopstick[i] = new Chopstick(i);
for(int i=0;i<size;i++)
executor.execute(new Philosopher(chopstick[i], chopstick[(i+1)%size], i, thinkingTime));
Thread.sleep(4*1000);
executor.shutdownNow();
}

}


运行结果:

分析运行结果会发现,

Philosopher [index=0]获得right stick[0],请求left stick

Philosopher [index=1]获得right stick[1],请求left stick

Philosopher [index=2]获得right stick[2],请求left stick

Philosopher [index=3]获得right stick[3],请求left stick

Philosopher [index=4]获得right stick[4],请求left stick

而没有一个能够同时获得两只筷子吃饭的哲学家。

Philosopher [index=0] thinking .......
Philosopher [index=3] thinking .......
Philosopher [index=4] thinking .......
Philosopher [index=1] thinking .......
Philosopher [index=4] start to eat and take right stick
Philosopher [index=3] start to eat and take right stick
Philosopher [index=2] thinking .......
Philosopher [index=0] start to eat and take right stick
Philosopher [index=2] start to eat and take right stick
Philosopher [index=3] take left stick
Philosopher [index=4] take left stick
Philosopher [index=1] start to eat and take right stick
Philosopher [index=2] take left stick
Philosopher [index=0] take left stick
Philosopher [index=1] take left stick
Philosopher [index=1]InterruptedException
Philosopher [index=0]InterruptedException
Philosopher [index=2]InterruptedException
Philosopher [index=3]InterruptedException
Philosopher [index=4]InterruptedException


产生死锁的条件:(简要)

互斥条件。至少有一个资源(筷子)只能同时被一个任务获得。

循环等待。这里每个哲学家都按照先获得右边的筷子,然后再获得左边的筷子的方式进行,那么就会构成一个循环等待。

修改后的测试类:

破坏第二个产生死锁的条件:循环等待。

将第五个哲学家就餐拿起筷子的顺序更改,不使之构成循环条件。

package philosopher;

import static org.junit.Assert.*;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Test;
/**
* 破坏产生死锁的循环条件
* 使第五个哲学家不按照先获得右边筷子,再获得左边筷子的方式进行
* @author lecky
*
*/
public class FixedPhilosopher {

@Test
public void test() throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
int size=5;
int thinkingTime = 0;
Chopstick[] chopsticks = new Chopstick[size];
for(int i=0;i<size;i++)
chopsticks[i]=new Chopstick(i);
for(int i=0;i<size-1;i++)
executor.execute(new Philosopher(chopsticks[i], chopsticks[i+1], i, thinkingTime));
executor.execute(new Philosopher(chopsticks[0], chopsticks[size-1], size, thinkingTime));//更改第五个哲学家获得筷子的顺序
Thread.sleep(100*1000);
executor.shutdownNow();
}

}


[1].thinking in java
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: