哲学家就餐问题(java全代码)

题目

有N个哲学家围坐在一张圆桌旁,桌上只有N把叉子,每对哲学家中间各有一把。

哲学家的两种行为:

一、思考

二、吃意大利面

哲学家只能拿起手边左边或右边的叉子

吃饭需要两把叉子

正确地模仿哲学家的行为

方法一

一次只允许四个人抢叉子
import java.util.concurrent.Semaphore;
class 方法一 {
    public static class PhilosopherTest {
        //一次只允许四个人抢叉子
        static final Semaphore count = new Semaphore(4);
        //五只叉子
        static final Semaphore[] mutex = {new Semaphore(1),
                                          new Semaphore(1),
                                          new Semaphore(1),
                                          new Semaphore(1),
                                          new Semaphore(1)};

        static class Philosopher extends Thread {

            Philosopher(int name) {
                super.setName(String.valueOf(name));
            }

            @Override
            public void run() {
                do {
                    try {
                        //只有四个人有抢叉子的资格
                        count.acquire();
                        Integer i = Integer.parseInt(super.getName());
                        //规定都先拿左手边的叉子,于是四个人左手都有叉子
                        mutex[i].acquire();
                        //大家开始抢右边的叉子
                        mutex[(i + 1) % 5].acquire();
                        //谁先抢到谁第一个开吃
                        System.out.println("哲学家" + i + "号吃饭!");
                        //吃完放下左手的叉子,对于左边人来说,就是他的右叉子,直接开吃
                        mutex[i].release();
                        //再放下右手的叉子
                        mutex[(i + 1) % 5].release();
                        //吃完了,开始思考,由于放下了右手的叉子,相当于给一个叉子没有的哲学家一个左叉子
                        count.release();
                        //模拟延迟
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        System.out.println("异常");
                    }
                } while (true);
            }
        }

        public static void main(String[] args) {

            Philosopher[] threads=new Philosopher[5];
            for (int i = 0; i < 5; i++) {
                threads[i] = new Philosopher(i);
            }

            for (Philosopher i : threads) {
                i.start();
            }
        }
    }
}

count每次acquire就会减一,使得第五个来访问的哲学家被阻塞

下面是将think和eat方法分离出来的改进版本:

import java.util.concurrent.Semaphore;
public class 方法一改进 {
        public static class PhilosopherTest {
            // 一次只允许四个人抢叉子
            static final Semaphore count = new Semaphore(4);
            // 五只叉子
            static final Semaphore[] mutex = {new Semaphore(1),
                    new Semaphore(1),
                    new Semaphore(1),
                    new Semaphore(1),
                    new Semaphore(1)};

            static class Philosopher extends Thread {

                Philosopher(int name) {
                    super.setName(String.valueOf(name));
                }

                @Override
                public void run() {
                    do {
                        try {
                            think();
                            eat();
                        } catch (InterruptedException e) {
                            System.out.println("异常");
                        }
                    } while (true);
                }

                public void think() throws InterruptedException {
                    // 模拟思考
                    System.out.println("哲学家" + super.getName() + "号正在思考");
                    Thread.sleep(2000); // 模拟延迟
                }

                public void eat() throws InterruptedException {
                    // 只有四个人有抢叉子的资格
                    count.acquire();
                    Integer i = Integer.parseInt(super.getName());
                    // 规定都先拿左手边的叉子,于是四个人左手都有叉子
                    mutex[i].acquire();
                    // 大家开始抢右边的叉子
                    mutex[(i + 1) % 5].acquire();
                    // 谁先抢到谁第一个开吃
                    System.out.println("哲学家" + i + "号吃饭!");
                    // 吃完放下左手的叉子,对于左边人来说,就是他的右叉子,直接开吃
                    mutex[i].release();
                    // 再放下右手的叉子
                    mutex[(i + 1) % 5].release();
                    // 吃完了,开始思考,由于放下了右手的叉子,相当于给一个叉子没有的哲学家一个左叉子
                    count.release();
                }
            }

            public static void main(String[] args) {

                PhilosopherTest.Philosopher[] threads=new PhilosopherTest.Philosopher[5];
                for (int i = 0; i < 5; i++) {
                    threads[i] = new PhilosopherTest.Philosopher(i);
                }

                for (PhilosopherTest.Philosopher i : threads) {
                    i.start();
                }
            }
        }
    }

本质没区别。

方法二

先获取左筷子,一段时间内申请不到右筷子就将左筷子释放
import java.util.concurrent.Semaphore;

public class 方法二 {
    //先获取左筷子,一段时间内申请不到右筷子就将左筷子释放
    // Five forks
    static final Semaphore[] mutex = {new Semaphore(1),
                                      new Semaphore(1),
                                      new Semaphore(1),
                                      new Semaphore(1),
                                      new Semaphore(1)};

    static class Philosopher extends Thread {

        Philosopher(int name) {
            super.setName(String.valueOf(name));
        }

        @Override
        public void run() {
            do {
                try {
                    Integer i = Integer.parseInt(super.getName());
                    //尝试获取左筷子
                    if (mutex[i].tryAcquire()) {
                        //尝试获取右筷子
                        if (mutex[(i + 1) % 5].tryAcquire()) {
                            System.out.println("哲学家" + i + "号吃饭!");
                            mutex[i].release();
                            mutex[(i + 1) % 5].release();
                            Thread.sleep(2000);
                        } else {
                            //如果获取不到右筷子,就把左筷子扔了
                            mutex[i].release();
                        }
                    }
                    //这里没有else,获取不到左筷子就一直尝试
                } catch (InterruptedException e) {
                    System.out.println("异常");
                }
            } while (true);
        }
    }

    public static void main(String[] args) {

        Philosopher[] threads=new Philosopher[5];
        for (int i = 0; i < 5; i++) {
            threads[i] = new Philosopher(i);
        }

        for (Philosopher i : threads) {
            i.start();
        }
    }
}

下面是将eat和think分出来的版本:

import java.util.concurrent.Semaphore;
class 方法二改进 {
    //先获取左筷子,一段时间内申请不到右筷子就将左筷子释放
    public static class Philosopher extends Thread {
        private static Semaphore[] chopsticks = {new Semaphore(1), new Semaphore(1), new Semaphore(1), new Semaphore(1), new Semaphore(1)};
        private int id;

        public Philosopher(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            while (true) {
                think();
                eat();
            }
        }

        public void think() {
            System.out.println("哲学家_" + this.id + "正在思考");

            //思考一秒时间
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void eat() {
            try {
                if (chopsticks[this.id].tryAcquire()) { // 获取左筷子
                    if (chopsticks[(this.id + 1) % chopsticks.length].tryAcquire()) { // 获取右筷子
                        System.out.println("哲学家_" + this.id + "正在吃饭");

                        // 吃饭花一秒时间
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            chopsticks[this.id].release(); // 放下左筷子
                            chopsticks[(this.id + 1) % chopsticks.length].release(); // 放下右筷子
                        }
                    } else {
                        chopsticks[this.id].release(); // 如果不能获取右筷子,释放左筷子
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public static void main(String[] args) {
           Philosopher[] threads=new Philosopher[5];
            for (int i = 0; i < 5; i++) {
                threads[i] = new Philosopher(i);
            }

            for (Philosopher i : threads) {
                i.start();
            }
        }
    }

}

下面是用可重入锁实现的版本:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Philosopher extends Thread {
    private Lock leftFork;
    private Lock rightFork;
    private int philosopherId;
    private int eatCount; // 计数器

    public Philosopher(int philosopherId, Lock leftFork, Lock rightFork) {
        this.philosopherId = philosopherId;
        this.leftFork = leftFork;
        this.rightFork = rightFork;
        this.eatCount = 0;
    }

    private void think() throws InterruptedException {
        System.out.println("Philosopher " + philosopherId + " is thinking.");
        Thread.sleep((long) (Math.random() * 1000));
    }

    private void eat() throws InterruptedException {
        System.out.println("Philosopher " + philosopherId + " is eating.");
        Thread.sleep((long) (Math.random() * 1000));
        eatCount++;
    }

    @Override
    public void run() {
        try {
            while (eatCount < 1) { // 表示每个哲学家吃了1次
                think();
                /* 使用ReentrantLock锁, 该类中有一个tryLock()方法, 在指定时间内获取不到锁对象, 就从阻塞队列移除,不用一直等待。
               当获取了左手边的筷子之后, 尝试获取右手边的筷子, 如果该筷子被其他哲学家占用, 获取失败, 此时就先把自己左手边的筷子,
               给释放掉. 这样就避免了死锁问题 */
                if (leftFork.tryLock()) {
                    System.out.println("Philosopher " + philosopherId + " picked up left fork.");
                    if (rightFork.tryLock()) {
                        System.out.println("Philosopher " + philosopherId + " picked up right fork.");
                        eat();
                        rightFork.unlock();
                        System.out.println("Philosopher " + philosopherId + " put down right fork.");
                    }
                    leftFork.unlock();
                    System.out.println("Philosopher " + philosopherId + " put down left fork.");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class DiningPhilosophers {
    public static void main(String[] args) {
        int numPhilosophers = 5;
        Philosopher[] philosophers = new Philosopher[numPhilosophers];
        Lock[] forks = new ReentrantLock[numPhilosophers];

        for (int i = 0; i < numPhilosophers; i++) {
            forks[i] = new ReentrantLock();
        }

        for (int i = 0; i < numPhilosophers; i++) {
            philosophers[i] = new Philosopher(i, forks[i], forks[(i + 1) % numPhilosophers]);
            philosophers[i].start();
        }

        // 等待所有哲学家线程结束
        for (Philosopher philosopher : philosophers) {
            try {
                philosopher.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("All philosophers have finished eating. Program ends.");
    }
}

  使用ReentrantLock锁, 该类中有一个tryLock()方法, 在指定时间内获取不到锁对象, 就从阻塞队列移除,不用一直等待。当获取了左手边的筷子之后, 尝试获取右手边的筷子, 如果该筷子被其他哲学家占用, 获取失败, 此时就先把自己左手边的筷子给释放掉. 这样就避免了死锁问题

 方法三

奇数哲学家先左后右,偶数科学家先右后左
import java.util.concurrent.Semaphore;

public class 方法四 {
    //奇数哲学家先左后右,偶数科学家先右后左
    // Five forks
    static final Semaphore[] mutex = { new Semaphore(1),
            new Semaphore(1),
            new Semaphore(1),
            new Semaphore(1),
            new Semaphore(1) };

    static class Philosopher extends Thread {

        Philosopher(int name) {
            super.setName(String.valueOf(name));
        }

        @Override
        public void run() {
            do {
                try {
                    Integer i = Integer.parseInt(super.getName());

                    if (i % 2 == 1) {
                        // Odd-numbered philosopher
                        // Try to acquire the left fork
                        mutex[i].acquire();
                        System.out.println("哲学家" + i + "号拿起左筷子");
                        // Try to acquire the right fork
                        mutex[(i + 1) % 5].acquire();
                        System.out.println("哲学家" + i + "号拿起右筷子");
                    } else {
                        // Even-numbered philosopher
                        // Try to acquire the right fork
                        mutex[(i + 1) % 5].acquire();
                        System.out.println("哲学家" + i + "号拿起右筷子");
                        // Try to acquire the left fork
                        mutex[i].acquire();
                        System.out.println("哲学家" + i + "号拿起左筷子");
                    }

                    // Eat
                    System.out.println("哲学家" + i + "号吃饭!");

                    // Release the forks
                    mutex[i].release();
                    mutex[(i + 1) % 5].release();

                    // Think (sleep for simulation)
                    Thread.sleep(2000);

                } catch (InterruptedException e) {
                    System.out.println("异常");
                }
            } while (true);
        }
    }

    public static void main(String[] args) {

        Philosopher[] threads = new Philosopher[5];
        for (int i = 0; i < 5; i++) {
            threads[i] = new Philosopher(i);
        }

        for (Philosopher i : threads) {
            i.start();
        }
    }
}

下面是将think和eat分开的版本:

import java.util.concurrent.Semaphore;
public class 方法四改进 {
    public static class 方法四 {
        //奇数哲学家先左后右,偶数科学家先右后左
        // Five forks
        static final Semaphore[] mutex = { new Semaphore(1),
                new Semaphore(1),
                new Semaphore(1),
                new Semaphore(1),
                new Semaphore(1) };

        static class Philosopher extends Thread {

            Philosopher(int name) {
                super.setName(String.valueOf(name));
            }

            @Override
            public void run() {
                do {
                    try {
                        think();
                        eat();
                    } catch (InterruptedException e) {
                        System.out.println("异常");
                    }
                } while (true);
            }

            public void think() throws InterruptedException {
                Integer i = Integer.parseInt(super.getName());
                System.out.println("哲学家" + i + "号正在思考");
                // Think (sleep for simulation)
                Thread.sleep(2000);
            }

            public void eat() throws InterruptedException {
                Integer i = Integer.parseInt(super.getName());

                if (i % 2 == 1) {
                    // Odd-numbered philosopher
                    // Try to acquire the left fork
                    mutex[i].acquire();
                    System.out.println("哲学家" + i + "号拿起左筷子");
                    // Try to acquire the right fork
                    mutex[(i + 1) % 5].acquire();
                    System.out.println("哲学家" + i + "号拿起右筷子");
                } else {
                    // Even-numbered philosopher
                    // Try to acquire the right fork
                    mutex[(i + 1) % 5].acquire();
                    System.out.println("哲学家" + i + "号拿起右筷子");
                    // Try to acquire the left fork
                    mutex[i].acquire();
                    System.out.println("哲学家" + i + "号拿起左筷子");
                }

                // Eat
                System.out.println("哲学家" + i + "号吃饭!");

                // Release the forks
                mutex[i].release();
                mutex[(i + 1) % 5].release();
            }
        }

        public static void main(String[] args) {

            Philosopher[] threads = new Philosopher[5];
            for (int i = 0; i < 5; i++) {
                threads[i] = new Philosopher(i);
            }

            for (Philosopher i : threads) {
                i.start();
            }
        }
    }

}

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值