多线程之死锁,哲学家就餐问题的实现
1.死锁是什么
死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
2.哲学家就餐问题
有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕,放下筷子又继续思考。
为了进一步阐述死锁的形成, 很多资料上也会谈论到 "哲学家就餐问题".
解决办法一,哲学家要进餐时,要么同时拿起两支筷子,要么一支筷子都不拿.
package thread4;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;//解决办法一,哲学家要进餐时,要么同时拿起两支筷子,要么一支筷子都不拿.
class Philosopher {//哲学家编号public int id;public Philosopher(int id) {this.id = id;}//思考public void thinking() throws InterruptedException {System.out.println("我是哲学家" + this.id + "号,我正在思考!");Thread.sleep(1000);}//吃饭public void eating() throws InterruptedException {System.out.println("我是哲学家" + this.id + "号,我正在吃饭!");Thread.sleep(1000);}//拿筷子public void takeUp(Chopsticks chopsticksLeft, Chopsticks chopsticksRight) throws InterruptedException {synchronized (Test3.locker) {if (chopsticksLeft.used || chopsticksRight.used) {Test3.locker.wait();}}chopsticksLeft.used = true;chopsticksRight.used = true;System.out.println("我是哲学家" + this.id + "号,我拿到俩只筷子!");}//放筷子public void putDown(Chopsticks chopsticksLeft, Chopsticks chopsticksRight) {synchronized (Test3.locker) {chopsticksLeft.used = false;chopsticksRight.used = false;System.out.println("我是哲学家" + this.id + "号,我吃完了!");Test3.locker.notify();}}
}//筷子
class Chopsticks {//筷子编号public int id;//筷子状态public boolean used;public Chopsticks(int id, boolean used) {this.id = id;this.used = used;}
}public class Test3 {public static Object locker = new Object();public static void main(String[] args) {Philosopher philosopher1 = new Philosopher(1);Philosopher philosopher2 = new Philosopher(2);Philosopher philosopher3 = new Philosopher(3);Philosopher philosopher4 = new Philosopher(4);Philosopher philosopher5 = new Philosopher(5);Chopsticks chopsticks1 = new Chopsticks(1, false);Chopsticks chopsticks2 = new Chopsticks(2, false);Chopsticks chopsticks3 = new Chopsticks(3, false);Chopsticks chopsticks4 = new Chopsticks(4, false);Chopsticks chopsticks5 = new Chopsticks(5, false);ExecutorService pool = Executors.newFixedThreadPool(5);pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher1.thinking();philosopher1.takeUp(chopsticks1, chopsticks2);philosopher1.eating();philosopher1.putDown(chopsticks1, chopsticks2);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher2.thinking();philosopher2.takeUp(chopsticks2, chopsticks3);philosopher2.eating();philosopher2.putDown(chopsticks2, chopsticks3);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher3.thinking();philosopher3.takeUp(chopsticks3, chopsticks4);philosopher3.eating();philosopher3.putDown(chopsticks3, chopsticks4);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher4.thinking();philosopher4.takeUp(chopsticks4, chopsticks5);philosopher4.eating();philosopher4.putDown(chopsticks4, chopsticks5);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher5.thinking();philosopher5.takeUp(chopsticks5, chopsticks1);philosopher5.eating();philosopher5.putDown(chopsticks5, chopsticks1);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.shutdown();}
}
解决办法二,给筷子编号,哲学家将要进餐时,要先从小号(左边)的开始拿.
package thread4;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;class Philosopher {//哲学家编号public int id;public Philosopher(int id) {this.id = id;}//思考public void thinking() throws InterruptedException {System.out.println("我是哲学家" + this.id + "号,我正在思考!");Thread.sleep(1000);}//吃饭public void eating() throws InterruptedException {System.out.println("我是哲学家" + this.id + "号,我正在吃饭!");Thread.sleep(1000);}//拿筷子public void takeUp(Chopsticks chopsticksMin, Chopsticks chopsticksMax) throws InterruptedException {synchronized (Test4.locker) {//先尝试拿小号筷子,失败则进入等待状态,并释放锁if (!chopsticksMin.used) {//false能拿,true不能拿别人占用的了所以 是!chopsticksMin.usedchopsticksMin.used = true;if (!chopsticksMax.used) {chopsticksMax.used = true;} else {Test4.locker.wait();}} else {Test4.locker.wait();}}}//放筷子public void putDown(Chopsticks chopsticksMin, Chopsticks chopsticksMax) {synchronized (Test4.locker) {chopsticksMin.used = false;chopsticksMax.used = false;System.out.println("我是哲学家" + this.id + "号,我吃完了!");Test4.locker.notify();}}
}//筷子
class Chopsticks {//筷子编号public int id;//筷子状态public boolean used;public Chopsticks(int id, boolean used) {this.id = id;this.used = used;}
}public class Test4 {public static Object locker = new Object();public static void main(String[] args) {Philosopher philosopher1 = new Philosopher(1);Philosopher philosopher2 = new Philosopher(2);Philosopher philosopher3 = new Philosopher(3);Philosopher philosopher4 = new Philosopher(4);Philosopher philosopher5 = new Philosopher(5);Chopsticks chopsticks1 = new Chopsticks(1, false);Chopsticks chopsticks2 = new Chopsticks(2, false);Chopsticks chopsticks3 = new Chopsticks(3, false);Chopsticks chopsticks4 = new Chopsticks(4, false);Chopsticks chopsticks5 = new Chopsticks(5, false);ExecutorService pool = Executors.newFixedThreadPool(5);pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher1.thinking();philosopher1.takeUp(chopsticks1, chopsticks2);philosopher1.eating();philosopher1.putDown(chopsticks1, chopsticks2);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher2.thinking();philosopher2.takeUp(chopsticks2, chopsticks3);philosopher2.eating();philosopher2.putDown(chopsticks2, chopsticks3);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher3.thinking();philosopher3.takeUp(chopsticks3, chopsticks4);philosopher3.eating();philosopher3.putDown(chopsticks3, chopsticks4);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher4.thinking();philosopher4.takeUp(chopsticks4, chopsticks5);philosopher4.eating();philosopher4.putDown(chopsticks4, chopsticks5);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.submit(new Runnable() {@Overridepublic void run() {try {philosopher5.thinking();philosopher5.takeUp(chopsticks5, chopsticks1);philosopher5.eating();philosopher5.putDown(chopsticks5, chopsticks1);} catch (InterruptedException e) {throw new RuntimeException(e);}}});pool.shutdown();}
}
3.如何避免死锁
死锁产生的四个必要条件
- 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
- 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
- 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
- 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路
当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。其中最容易破坏的就是 "循环等待".
破坏循环等待
最常用的一种死锁阻止技术就是锁排序. 假设有 N 个线程尝试获取 M 把锁, 就可以针对 M 把锁进行编号(1, 2, 3...M).N 个线程尝试获取锁的时候, 都按照固定的按编号由小到大顺序来获取锁. 这样就可以避免环路等待.
属于理论上的破除死锁的方法.但是这样的方法,并不实用.
更实用的方法,就是尽量避免锁嵌套.(不要在一个加锁的代码中,再去加其他锁...)
可能产生环路等待的代码:
package thread4;public class Test2 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();Thread thread1 = new Thread() {@Overridepublic void run() {synchronized (locker1) {synchronized (locker2) {}}}};thread1.start();Thread thread2 = new Thread() {@Overridepublic void run() {synchronized (locker2) {synchronized (locker1) {}}}};thread2.start();}
}
约定好先获取 lock1, 再获取 lock2 , 就不会环路等待(锁排序)
package thread4;public class Test2 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();Thread thread1 = new Thread() {@Overridepublic void run() {synchronized (locker1) {synchronized (locker2) {}}}};thread1.start();Thread thread2 = new Thread() {@Overridepublic void run() {synchronized (locker1) {synchronized (locker2) {}}}};thread2.start();}
}
相关文章:

多线程之死锁,哲学家就餐问题的实现
1.死锁是什么 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。 2.哲学家就餐问题 有五个哲学家,他们的生活方式是交替地进行思考和进餐…...

UTF-8编码
介绍 UTF-8 编码 UTF-8 是一种针对 Unicode 的可变长度字符编码。 针对 Unicode:UTF-8 是 Unicode 的实现方式之一。相当于 Unicode 规定了字符对应的代码值,这个代码值需要转换为字节序列的形式,用于数据存储、传输。代码值到字节序列的转…...

likeshop单商户SaaS版V1.8.2说明!
likeshop单商户SaaS版V1.8.2主要更新如下: 新增 前端登录引导用户填写头像昵称 PC端—注册页面显示服务协议和隐私政策 PC端—首次进入商城弹出协议提示 PC端—结算页新增门店自提的配送方式 后台—PC端菜单导航栏的跳转链接支持添加自定义链接 优…...

算法训练营 day46 动态规划 最后一块石头的重量 II 目标和 一和零
算法训练营 day46 动态规划 最后一块石头的重量 II 目标和 一和零 最后一块石头的重量 II 1049. 最后一块石头的重量 II - 力扣(LeetCode) 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合…...

nginx-host绕过实例复现
绕过Nginx Host限制第一种处理方法Nginx在处理Host的时候,会将Host用冒号分割成hostname和port,port部分被丢弃。所以,我们可以设置Host的值为2023.mhz.pw:xxx"example.com,这样就能访问到目标Server块:第二种处理…...
Java学习记录day9
类与对象 内部类 成员内部类 在一个类的内部定义的类。 public class Outer {private int a 10;public void outMethod() {System.out.println("这是外部类中的方法");}// 成员内部类public class Inner{private int b 10;public void innerMethod() {// 外部类…...

ActiveReports.NET 17.0 Crack by Xacker
一个完整的报告解决方案,用于在您的业务应用程序中设计、定制、发布和查看报告。 ActiveReports.NET 通过直观的 Visual Studio 集成报表设计器和丰富的控件帮助您提供精美的报表。ActiveReports 提供基于代码的跨平台报告、易于使用的设计器和灵活的 API。适用于桌…...

【计算机网络】传输层TCP协议
文章目录认识TCP协议TCP协议的格式字段的含义序号与确认号六个标志位窗口大小确认应答(ACK)机制超时重传机制连接管理机制三次握手四次挥手滑动窗口流量控制拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况总结认识TCP协议 传输控制协议 (TCP,T…...

Mysql5.7安装【Windows版】
文章目录一、下载二、添加到环境变量三、添加配置文件my.ini四、安装Mysql 修改密码一、下载 下载地址 滑倒最下面有一个MySQL Community Server 选择要下载的版本 二、添加到环境变量 下载好了之后开始解压 把bin目录添加到环境变量 可以点击进入bin目录,直接复…...

分布式一致性算法Raft原理图释
什么是分布式一致性算法Raft 分布式一致性算法Raft:指在分布式场景下实现集群数据同步的解决方案 掌握了这个算法,就可以较容易地处理绝大部分场景的容错和数据一致性需求 Raft三大角色 跟随者(Follower):普通群众…...

网络安全-字典生成-crunch
网络安全-字典生成-crunch crunch工具,在kali已经集成好了 2是代表最小字符长度 4是最大字符长度 生成了一个2M的文件 还有我们来查看这个密码本 从abcd26个英文字母的2位到4位的组合,他全部排列了一次 还可以自定义数字,特殊字符…...
闪光桐人の实习日记
2023年2月13日 1,认识了职场礼仪,学习了职场礼仪的重要性 尊重->心情愉悦->建立信任与好感->合作机遇的敲门砖 2,学习了职场礼仪中的邮件礼仪 模板管理中设置自己的名片 部门写到三级部,如果部门名太长要换一行 发送…...
PostgreSQL 常见配置参数
max_wal_size : 两个检查点(checkpoint)之间,WAL可增长的最大大小,即:自动WAL checkpoint允许WAL增长的最大值。该值缺省是1GB。如果提高该参数值会提升性能,但也是会消耗更多空间、同时会延长崩溃恢复所需…...

JAVA 常用类型之String结构
String在java中我们是用来操作字符串的,但它的底层结构确是一个char[]数组,通过数组的方式将每个字符进行保存。 使用时:String str"ABCD",内部存value确是:value[A,B,C,D]; 如下图: 参考String源…...

二三层网络设备封装与解封装原理
1、寻址转发(寻址指的是寻找IP地址) 路由表放在一个公共的地方,比如主控板上,由主控板 的CPU运行路由协议,计算路由,生成和维护路由表。 转发表与路由表: 转发表是根据路由表生成的。路由表中…...

9、MyBatis框架——使用注解开发实现数据库增删改查操作、一级缓存、二级缓存、MyBatis实现分页
目录 一、使用注解开发实现数据库增删改查操作 1、搭建项目 2、使用注解开发操作数据库 二、一级缓存 1、一级缓存失效的情况 三、二级缓存 1、手动开启二级缓存cacheEnabled 2、二级缓存机制 四、MyBatis实现分页 1、配置环境 2、startPage()开启分页 3、PageInfo…...

C++STL剖析(六)—— set和multiset的概念和使用
文章目录🌟 前言🍑 树型结构和哈希结构🍑 键值对1. set的介绍和使用🍑 set的模板参数列表🍑 set的构造🍑 set的使用🍅 insert🍅 find🍅 erase🍅 swap…...

SpringColud第四讲 Nacos的Windows安装方式和Linux的安装方式
在Nacos的GitHub页面,提供有下载链接,可以下载编译好的Nacos服务端或者源代码: 目录 1.Windows安装Nacos 1.1.下载 1.2.解压 1.3.修改相关配置: 1.4.启动: 1.5.登录: 2.Linux的安装方式Nacos 2.1.…...

微服务项目【网关服务限流熔断降级分布式事务】
网关服务限流熔断降级 第1步:启动sentinel-dashboard控制台和Nacos注册中心服务 第2步:在网关服务中引入sentinel依赖 <!-- sentinel --> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-…...

【情人节用Compose给女神写个爱心动画APP】
情人节用Compose给女神写个爱心动画APP前言涉及知识点实现思路实现过程绘制爱心创建动画效果Preview预览效果完整源码彩蛋前言 前一阵子看电视里的学霸用代码写了个炫酷的爱心,网上有很多js和python的源码,复制粘贴就能拥有,但是Android的好…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...