多线程之哲学家就餐问题

本文探讨了多线程中的哲学家就餐问题,详细解释了死锁产生的原因,并通过Java代码展示了如何使用ReentrantLock避免死锁。通过设定特定的筷子获取顺序,成功解决了这个问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多线程之哲学家就餐问题

1、问题描述

在这里插入图片描述
在这里插入图片描述

2、死锁

2.1产生死锁的原因

吃饭需要使用两根筷子才可以吃饭,哲学家需要先拿起一边的筷子,再去拿另一边的筷子。假如这个时候每个哲学家都同时拿起了右手边的筷子,去拿左手边筷子的时候,发现左手边的筷子被人拿走了,就需要等待,所有哲学家都在等待左手边的筷子,就造成了死锁。

2.2产生死锁的代码

java是面向对象的语言,所以需要从问题中抽出对象来进行代码的编写。由问题描述中得到两个对象:筷子和哲学家。

Chopstiack.java

/**
 * 筷子
 */
public class Chopstick {
    private String name;

    public Chopstick(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "name='" + name + '\'' +
                '}';
    }
}

Philosopher .java

哲学家类需要去拿筷子,每个哲学家只负责拿自己的筷子,也就是一个哲学家代表一个线程,所以需要继承Thread类。

/**
 * 哲学家类
 */
public class Philosopher extends Thread{
    Chopstick left;
    Chopstick right;

    public Philosopher(String name, Chopstick left, Chopstick right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (left) {
                synchronized (right) {
                    eat();
                }
            }
        }
    }

    private void eat(){
        System.out.println(Thread.currentThread().getName() + "eating...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Test.java

测试类

public class TestDeadLock {
    public static void main(String[] args) {
        Chopstick c1 = new Chopstick("1");
        Chopstick c2 = new Chopstick("2");
        Chopstick c3 = new Chopstick("3");
        Chopstick c4 = new Chopstick("4");
        Chopstick c5 = new Chopstick("5");

        new Philosopher("苏格拉底", c1, c2).start();
        new Philosopher("柏拉图", c2, c3).start();
        new Philosopher("亚里士多德", c3, c4).start();
        new Philosopher("赫拉克利特", c4, c5).start();
        new Philosopher("阿基米德", c5, c1).start();
    }
}

运行结果
在这里插入图片描述
查看死锁

下面每一种颜色的框代表着一个死锁,
在这里插入图片描述

3、解决办法

3.1 方法

使用ReentrantLock,哲学家首先获取左边的筷子,然后去获取右边的筷子,如果无法获取右边的筷子,就释放左边的筷子。这样就可以打破死锁。

3.2代码

Chopstick.java

/**
 * 筷子类
 */
public class Chopstick extends ReentrantLock {
    private String name;

    public Chopstick(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "name='" + name + '\'' +
                '}';
    }
}

Philosopher.java

/**
 * 哲学家类
 */
public class Philosopher extends Thread{
    private Chopstick left;
    private Chopstick right;

    public Philosopher(String name, Chopstick left, Chopstick right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    /**
     * Chopstick是可重入锁,如果获取了左筷子就可以获取右筷子。
     */
    @Override
    public void run() {
        while (true) {
            if(left.tryLock()) {
                try {
                    if(right.tryLock()) {
                        try {
                            eat();
                        }finally {
                            right.unlock();
                        }
                    }
                }finally {
                    left.unlock();
                }
            }
        }
    }

    public void eat() {
        System.out.println(Thread.currentThread().getName() + "eating");
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        Chopstick c1 = new Chopstick("1");
        Chopstick c2 = new Chopstick("2");
        Chopstick c3 = new Chopstick("3");
        Chopstick c4 = new Chopstick("4");
        Chopstick c5 = new Chopstick("5");

        new Philosopher("苏格拉底", c1, c2).start();
        new Philosopher("柏拉图", c2, c3).start();
        new Philosopher("亚里士多德", c3, c4).start();
        new Philosopher("赫拉克利特", c4, c5).start();
        new Philosopher("阿基米德", c5, c1).start();
    }
}

3.3 执行结果

在这里插入图片描述
从下面的图里面可以看出来,没有产生死锁
在这里插入图片描述

4、其他解决办法

1、可以给哲学家设置右撇子,即有一个哲学家先去拿右手边的筷子,再去拿左手边的筷子;其他四位哲学家先拿左手边的筷子,再去拿右手边的筷子;
2、根据哲学家的下标进行分类,奇数先拿左边的筷子,再去拿右边的筷子;偶数先去拿右边的筷子,再去拿左边的筷子。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值