Java基础-并发编程-哲学家就餐问题

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();
        }
    }

}

解决哲学家就餐问题办法:

  1. 每个哲学家必须确定自己左右手的筷子都可用的时候,才能同时拿起两只筷子进餐,吃完之后同时放下两只筷子。以上演示死锁就是使用该方案。
  2. 规定每个哲学家拿筷子时必须拿序号小的那只,这样最后一位未拿到筷子的哲学家只剩下序号大的那只筷子,不能拿起,剩下的这只筷子就可以被其他哲学家使用,避免了死锁。这种情况不能很好的利用资源。

解放方法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();
            }
        }
    }
 
}

你可能感兴趣的:(Java基础-并发编程-哲学家就餐问题)