死锁:哲学家就餐问题

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值