多线程面试题——哲学家就餐问题(Java)

  • class : 哲学家 class : 筷子
  • 筷子:编号
  • 哲学家:左手的筷子 右手的筷子 编号

代码实现

package com.mashibing.juc.c_33_TheDinningPhilosophersProblem;

import com.mashibing.util.SleepHelper;

public class T01_DeadLock {

public static void main(String[] args) {

// 构建过程

// 1.创建5根筷子

ChopStick cs0 = new ChopStick();

ChopStick cs1 = new ChopStick();

ChopStick cs2 = new ChopStick();

ChopStick cs3 = new ChopStick();

ChopStick cs4 = new ChopStick();

// 2.创建5个线程(哲学家)

Philosohper p0 = new Philosohper(“p0”, 0, cs0, cs1);// 将cs0给第0号的左手,将cs1给第0号的右手

Philosohper p1 = new Philosohper(“p1”, 1, cs1, cs2);// 将cs1也给第1号的左手,cs2给第1号的右手

Philosohper p2 = new Philosohper(“p2”, 2, cs2, cs3);// …

Philosohper p3 = new Philosohper(“p3”, 3, cs3, cs4);

Philosohper p4 = new Philosohper(“p4”, 4, cs4, cs0);

p0.start();

p1.start();

p2.start();

p3.start();

p4.start();

}

// 哲学家就是一个线程,我们定义类的时候直接继承Thread类

public static class Philosohper extends Thread {

private ChopStick left, right;

private int index;

public Philosohper(String name, int index, ChopStick left, ChopStick right) {

this.setName(name);

this.index = index;

this.left = left;

this.right = right;

}

@Override

public void run() {

// 将左手的筷子上锁

synchronized (left) {

SleepHelper.sleepSeconds(1 + index);

// 抢右手的筷子(锁定A再抢B)

synchronized (right) {

SleepHelper.sleepSeconds(1);

System.out.println(index + " 号 哲学家已经吃完");

}

}

}

}

}

但是我们执行后,程序没有做任何的输出,说明死锁已经形成了。

image-20211222214721246

死锁一般具有2把以上的锁,在锁定1把的时候等待另外1把锁。

效率不高的解法


  • 线程粗化:一次性拿到所有资源,把锁的范围放大。

  • 我们可以看到,锁是有5把的,5把锁互相之间做同步,太复杂了,我们干脆把5把锁合并成1把锁,线程只有抢到这1把大锁的时候才可以执行。我们可以把5个筷子存入数组,或者5个筷子的对象,直接锁定整个数组或者这个大对象。

  • 但是这个效率是不高的,我们明明可以只拿到2个筷子就可以夹菜,但这样明显是拿到了5个筷子,只有1个人可以夹菜,效率比较低。

  • 但有的人可能会想到:将5个筷子都存放在数组里,哲学家每次使用只从数组里拿出2个筷子,这样就可以保证不冲突了吗?肯定是不可以的,因为你没法保证这几个线程分别拿到的筷子是否冲突,这样的想法还是存在问题的。

  • 最少保证(很简单的解法):哲学家有一个是左撇子(先这么理解)。

  • 因为之前哲学家都是先拿左手的筷子,再去抢右手的筷子,而此时,这个左撇子的哲学家,会先去拿右手的筷子,再去抢左手的筷子。当左撇子拿到了a筷子时,哲学家1号想拿左手边的筷子(也就是a筷子)是拿不到的,所以他一定不会去拿b筷子,2、3、4号三位哲学家分别都可以拿到左手边(也就是b、c、d)筷子,但要注意4号,e筷子这里分为两种情况,①:e筷子被4号拿到,此时4号已经有了两个筷子,4号吃完,将d、e筷子解锁,左撇子和3号都可以使用两个筷子了,从而死锁被解开。②:e筷子被左撇子拿到,死锁同样被解开。

image-20211225142722278

  • 但是这个效率也不是很高,因为只有一个人可以先吃,然后才能将锁依次解开,这里只有5个哲学家,假如有500个,5000个呢?依次解开需要花费很长的时间。

最后

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考

以下是部分内容截图
架构面试专题及架构学习笔记导图.png

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到*

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考

以下是部分内容截图
[外链图片转存中…(img-Iiv4NI5P-1714670870548)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值