美文网首页Java工程师知识树
Java基础-并发编程-哲学家就餐问题

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

作者: HughJin | 来源:发表于2021-02-22 09:18 被阅读0次

    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基础-并发编程-哲学家就餐问题

        本文链接:https://www.haomeiwen.com/subject/zccqtltx.html