Java工程师知识树 / Java基础
问题描述:
一圆桌前坐着5位哲学家,两个人中间有一只筷子,桌子中央有面条。
哲学家思考问题,当饿了的时候拿起左右两只筷子吃饭,必须拿到两只筷子才能吃饭。
上述问题会产生死锁的情况,当5个哲学家都拿起自己右手边的筷子,准备拿左手边的筷子时产生死锁现象。
死锁情况演示
package com.thread.study;
public class DiningPhilosophers{
public static class Philosopher implements Runnable {
private Object leftChopstick;
private Object rightChopstick;
public Philosopher(Object leftChopstick, Object rightChopstick) {
this.leftChopstick = leftChopstick;
this.rightChopstick = rightChopstick;
}
@Override
public void run() {
try {
while (true) {
doAction("思考生命、宇宙......");
synchronized (leftChopstick) {
doAction("拿起左边的筷子");
synchronized (rightChopstick){
doAction("拿起右边的筷子 -- 开始吃饭");
doAction("放下右边的筷子");
}
doAction("放下左边的筷子");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void doAction(String action) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + action);
Thread.sleep((long) (Math.random() * 10));
}
}
public static void main(String[] args) {
Philosopher[] philosophers = new Philosopher[5];
Object[] chopsticks = new Object[5];
for (int i = 0; i < chopsticks.length; i++) {
chopsticks[i] = new Object();
}
for (int i = 0; i < philosophers.length; i++) {
Object leftChopstick = chopsticks[i];
Object rightChopstick = chopsticks[(i+1)%philosophers.length];
philosophers[i] = new Philosopher(leftChopstick, rightChopstick);
new Thread(philosophers[i],"哲学家"+(i+1)+"号").start();
}
}
}
解决死锁方法
package com.thread.study;
public class DiningPhilosophers{
public static class Philosopher implements Runnable {
private Object leftChopstick;
private Object rightChopstick;
public Philosopher(Object leftChopstick, Object rightChopstick) {
this.leftChopstick = leftChopstick;
this.rightChopstick = rightChopstick;
}
@Override
public void run() {
try {
while (true) {
doAction("思考生命、宇宙......");
synchronized (leftChopstick) {
doAction("拿起左边的筷子");
synchronized (rightChopstick){
doAction("拿起右边的筷子 -- 开始吃饭");
doAction("放下右边的筷子");
}
doAction("放下左边的筷子");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void doAction(String action) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + action);
Thread.sleep((long) (Math.random() * 10));
}
}
public static void main(String[] args) {
Philosopher[] philosophers = new Philosopher[5];
Object[] chopsticks = new Object[5];
for (int i = 0; i < chopsticks.length; i++) {
chopsticks[i] = new Object();
}
// for (int i = 0; i < philosophers.length; i++) {
// Object leftChopstick = chopsticks[i];
// Object rightChopstick = chopsticks[(i+1)%philosophers.length];
// philosophers[i] = new Philosopher(leftChopstick, rightChopstick);
// new Thread(philosophers[i],"哲学家"+(i+1)+"号").start();
// }
//在上面的代码上进行修改
for (int i = 0; i < philosophers.length; i++) {
Object leftChopstick = chopsticks[i];
Object rightChopstick = chopsticks[(i+1)%philosophers.length];
if (i == philosophers.length - 1) {//改变其中一位哲学家拿筷子的顺序,避免死锁
philosophers[i] = new Philosopher(rightChopstick, leftChopstick);
}else {
philosophers[i] = new Philosopher(leftChopstick, rightChopstick);
}
new Thread(philosophers[i],"哲学家"+(i+1)+"号").start();
}
}
}
解决哲学家就餐问题办法:
- 每个哲学家必须确定自己左右手的筷子都可用的时候,才能同时拿起两只筷子进餐,吃完之后同时放下两只筷子。以上演示死锁就是使用该方案。
- 规定每个哲学家拿筷子时必须拿序号小的那只,这样最后一位未拿到筷子的哲学家只剩下序号大的那只筷子,不能拿起,剩下的这只筷子就可以被其他哲学家使用,避免了死锁。这种情况不能很好的利用资源。
解放方法2
package com.thread.study;
public class PhilosopherQuestion {
// chopsticks
private boolean[] used = new boolean[] { false, false, false, false, false };
private static String LOCK = "static_class_lock";
public static void main(String[] args) {
PhilosopherQuestion philosopherQuestion = new PhilosopherQuestion();
philosopherQuestion.new Philosopher1(0).start();
philosopherQuestion.new Philosopher1(1).start();
philosopherQuestion.new Philosopher1(2).start();
philosopherQuestion.new Philosopher1(3).start();
philosopherQuestion.new Philosopher1(4).start();
}
class Philosopher1 extends Thread {
private int num;
public Philosopher1(int num) {
this.num = num;
}
public void eating() {
System.out.println("my num is " + num + " , I am eating...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void thinking() {
System.out.println("my num is " + num + ", I am thinking...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void takeChopsticks() {
synchronized (LOCK) {
int min = Math.min(num, (num + 1) % 5);
int max = Math.max(num, (num + 1) % 5);
if (!used[min]) {//先尝试拿小号筷子,失败则进入等待状态,并释放锁
used[min] = true;
if(!used[max]) {//成功拿到小号筷子后,尝试拿大号筷子,失败,则进入等待状态,并释放锁,但是没有释放小号筷子
used[max] = true;
} else {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//放下筷子后,通知其他等待线程
public void putDownChopsticks() {
synchronized(LOCK) {
used[num] = false;
used[(num + 1) % 5] = false;
System.out.println("my num is " + num + " , I have finished...");
LOCK.notifyAll();
}
}
@Override
public void run() {
while(true) {
thinking();
takeChopsticks();
eating();
putDownChopsticks();
}
}
}
}