有5个哲学家围着一张圆桌子,在每人的左右都有一根筷子,只有凑够两根筷子才可以吃饭,哲学家只有思考和吃饭两个动作,模拟这个过程,探究死锁的产生。
如果每个哲学家开始都是先拿起左边的筷子,等待着右边的人放下另外一根筷子,所有的哲学家都拿着一根筷子,所有的哲学家都在等筷子,这样就进入死锁的状态。
看一下代码实现:
哲学家类:
class Philosopher extends ReentrantLock implements Runnable{
private String name;
private volatile Chopstick leftChopstick;
private volatile Chopstick rightChopstick;
public Philosopher(String name, Chopstick leftChopstick, Chopstick rightChopstick) {
this.name = name;
this.leftChopstick = leftChopstick;
this.rightChopstick = rightChopstick;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Chopstick getLeftChopstick() {
return leftChopstick;
}
public void setLeftChopstick(Chopstick leftChopstick) {
this.leftChopstick = leftChopstick;
}
public Chopstick getRightChopstick() {
return rightChopstick;
}
public void setRightChopstick(Chopstick rightChopstick) {
this.rightChopstick = rightChopstick;
}
public void think() {
System.out.println("哲学家:"+name+" 正在思考");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void eat() {
while (true) {
if(!leftChopstick.isHeld()) {
System.out.println("哲学家: "+name+"拿起了左边筷子:"+leftChopstick.getId());
leftChopstick.setHeld(true);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println(leftChopstick.getId()+" "+leftChopstick.isHeld());
break;
} else {
think();
}
}
while (true) {
if(!rightChopstick.isHeld()) {
System.out.println("哲学家: "+name+"拿起了右边筷子:"+rightChopstick.getId());
rightChopstick.setHeld(true);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println(rightChopstick.getId()+" "+rightChopstick.isHeld());
break;
} else {
think();
}
}
System.out.println("哲学家:" + name + "开始吃饭");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("哲学家: "+ name + "吃完");
leftChopstick.setHeld(false);
rightChopstick.setHeld(false);
}
@Override
public void run() {
eat();
}
}
筷子类:
class Chopstick {
private int id;
AtomicBoolean isHeld;
public Chopstick(int id, boolean isHeld) {
this.id = id;
this.isHeld = new AtomicBoolean(isHeld);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public boolean isHeld() {
return isHeld.get();
}
public void setHeld(boolean held) {
isHeld.getAndSet(held);
}
}
测试类:
public class PhilosopherEatTest {
public static void main(String[] args) {
Chopstick[] chopsticks = new Chopstick[5];
for (int i = 0; i < 5; i++) {
chopsticks[i] = new Chopstick(i,false);
}
Philosopher[] philosophers = new Philosopher[5];
for (int i = 0; i < 5; i++) {
philosophers[i] = new Philosopher(" "+i,chopsticks[i],chopsticks[(i+1)%5]);
}
for (int i = 0; i < 5; i++) {
new Thread(philosophers[i]).start();
}
}
}
我的实现思路就是每个哲学家线程默认先去尝试拿左边的筷子,拿筷子是需要时间的,这个时候所有的线程都拿起了左边的筷子,而右边的筷子都被右边的哲学家拿起,这样就出现了僵持状态,出现了死锁。
这个就是模拟过程。
还有两种解决方案
仲裁者解决方法
public class Philosoper1 implements Runnable {
private Stick1 fork;
private int name;
private Random random;
public Philosoper1(Stick1 fork, int name) {
this.fork = fork;
this.name = name;
this.random = new Random();
}
/**
* 思考
*/
private void doThinking() {
System.out.println("哲学家:"+name+"在思考");
try {
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 吃饭
*/
private void doEating() {
System.out.println("哲学家:"+name+"在吃饭");
try {
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while (true) {
doThinking();
fork.takeFork(name);
doEating();
fork.putDownFork(name);
}
}
public static void main(String[] args) {
Stick1 fork = new Stick1();
Philosoper1 p0 = new Philosoper1(fork,0);
Philosoper1 p1 = new Philosoper1(fork,1);
Philosoper1 p2 = new Philosoper1(fork,2);
Philosoper1 p3 = new Philosoper1(fork,3);
Philosoper1 p4 = new Philosoper1(fork,4);
ExecutorService threadPool = Executors.newCachedThreadPool();
threadPool.execute(p0);
threadPool.execute(p1);
threadPool.execute(p2);
threadPool.execute(p3);
threadPool.execute(p4);
threadPool.shutdown();
}
}
chandy/misra解决
public class Philosoper2 implements Runnable {
//哲学家的左边筷子,右边筷子
private Stick2 leftStick,rightStick;
//哲学家左边临座、右边邻座
private Philosoper2 leftPhilo,rightPhilo;
//哲学家编号
private int num;
//产生随机数
private Random random;
public Philosoper2(int num) {
this.num = num;
random = new Random();
}
/**
* 请求获取筷子
* @param stick
*/
public void answer(Stick2 stick) {
synchronized (this) {
if (stick.isStatus()) {
//筷子是脏的
if (stick == leftStick) {
stick.setOwner(leftPhilo.num);
} else if (stick == rightStick) {
stick.setOwner(rightPhilo.num);
} else {
System.out.println("stick isn't this philo");
}
stick.setStatus(false);
} else {
System.out.println("stick isn't dirty");
}
}
}
/**
* 哲学家就餐
*/
public void eating() {
try {
Thread.sleep(random.nextInt(3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
leftStick.setStatus(true);
rightStick.setStatus(true);
System.out.println("philo:"+num+" is eating...");
}
public void run() {
while (true) {
while (leftStick.getOwner() != num || rightStick.getOwner() != num) {
if (leftStick.getOwner() != num) {
leftPhilo.answer(leftStick);
} else {
rightPhilo.answer(rightStick);
}
}
synchronized (this) {
if (leftStick.getOwner() == num && rightStick.getOwner() == num) {
eating();
}
}
try {
Thread.sleep(random.nextInt(4000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//初始化5个
initStickAndPhilo(5);
}
/**
* 对哲学家和筷子数量初始化编号等
* @param num:哲学家和筷子数量
*/
private static void initStickAndPhilo(int num) {
if (num < 2) {
System.out.println("参数有误");
return;
}
Stick2[] ss = new Stick2[num];
Philosoper2[] ps = new Philosoper2[num];
for (int i = 0; i < num; i++) {
ss[i] = new Stick2(i, true);
ps[i] = new Philosoper2(i);
}
for (int i = 0; i < num; i++) {
ps[i].leftPhilo = ps[(i+(num-1))%num];
ps[i].rightPhilo = ps[(i+1)%num];
ps[i].leftStick = ss[i];
ps[i].rightStick = ss[(i+1)%num];
}
for (int i = 0; i < num; i++) {
new Thread(ps[i]).start();
}
}
}