哲学家就餐问题(Java实现信号量和PV操作)

哲学家就餐是经典的PV操作。

一个哲学家同时拿起左边的筷子和右边的筷子进行就餐,每一个哲学家都会等待右边的筷子,具备了死锁问题之一的循环等待。

基础的哲学家就餐问题代码

在Java中,Semaphore 是一个用于控制对某个资源的访问的同步工具。它主要用于管理对有限资源的访问,确保在多线程环境中安全地控制资源的使用。

  • 信号量Semaphore 是一个计数信号量,维护一个计数器,表示可用资源的数量。
  • 获取与释放:线程可以通过 acquire() 方法获取一个许可,当资源可用时,计数器减一;如果没有可用资源,线程会被阻塞。释放资源时,使用 release() 方法,计数器加一。
  • acquire()就是p操作,获取资源。
  • release()就是v操作,用来释放资源。
import java.util.Arrays;
import java.util.concurrent.Semaphore;

public class PhilosopherMeal extends Thread {

    private static String name = "哲学家";
    private int philosopherNum; //哲学家编号
    public static Semaphore[] semaphores = new Semaphore[5];

    public PhilosopherMeal(int philosopherNum) {
        //调用父类的Thread创建线程
        super(PhilosopherMeal.name + String.valueOf(philosopherNum));
        this.philosopherNum = philosopherNum;

    }

    @Override
    public void run() {
        try {
            semaphores[this.philosopherNum].acquire();  //获取锁
            Thread.sleep((long) (Math.random() * 1));   //拿起筷子的时间,会发生死锁
            //acquire(): 获取一个许可。如果信号量的许可已经被其他线程占用,那么当前线程会阻塞,直到有许可可用。
            semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();
            System.out.println(this.getName() + "获取到了筷子" + String.valueOf(this.philosopherNum) +
                               "和" + String.valueOf(this.getRightSemaphoreIndex(this.philosopherNum)) + "开始吃饭了");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放一个许可,意味着当前线程已不再占用资源,其他线程可以继续使用该资源。
            semaphores[(this.philosopherNum)].release();
            semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].release();
        }
    }
    //哲学家拿起右边的筷子吃饭
    private int getRightSemaphoreIndex(int philosopherNum) {
        return (philosopherNum - 1 < 0) ? semaphores.length - 1 : philosopherNum - 1;
    }

    public static void main(String[] args) {
        // Arrays.fill(semaphores,new Semaphore(1));
        for (int i = 0; i < semaphores.length; i++) {
            semaphores[i] = new Semaphore(1);
        }
        for (int i = 0; i < 5; i++) {
            new PhilosopherMeal(i).start();
        }
    }
}

发生死锁

没有发生死锁

解决方案

1.打破死锁的条件之一 循环条件等待,我们可以破坏循环的条件从而来解决死锁。

让奇数的筷子先拿左边的筷子,后拿右边的筷子。

偶数的筷子先拿右边的筷子,后那左边的筷子,这样就可以解决死锁的问题。破快了相互之间循环等待的条件。

@Override
public void run() {
    try {
        int n = this.philosopherNum; //拿到哲学家的编号
        if (n % 2 == 1) {  //奇数的先拿左边筷子 偶数拿右边
            semaphores[this.philosopherNum].acquire();  //获取锁
            Thread.sleep((long) (1000));   //拿起筷子的时间,会发生死锁
            semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();
        } else {
            semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();
            Thread.sleep((long) (1000));   //拿起筷子的时间,会发生死锁
            semaphores[this.philosopherNum].acquire();  //获取锁
        }
        //acquire(): 获取一个许可。如果信号量的许可已经被其他线程占用,那么当前线程会阻塞,直到有许可可用。
        System.out.println(this.getName() + "获取到了筷子" + String.valueOf(this.philosopherNum) +
                           "和" + String.valueOf(this.getRightSemaphoreIndex(this.philosopherNum)) + "开始吃饭了");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //释放一个许可,意味着当前线程已不再占用资源,其他线程可以继续使用该资源。
        semaphores[(this.philosopherNum)].release();
        semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].release();
    }
}

2.最多只能让四个哲学家同时拿起筷子,这样就保证了一定有一个哲学家可以拿到筷子。这样就不会发生死锁。

    private static Semaphore count = new Semaphore(4);
@Override
public void run() {
    try {
        count.acquire(); //获取count
        semaphores[this.philosopherNum].acquire();  //获取锁
        Thread.sleep(1000);
        semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();
        System.out.println(this.getName() + "获取到了筷子" + String.valueOf(this.philosopherNum) +
                           "和" + String.valueOf(this.getRightSemaphoreIndex(this.philosopherNum)) + "开始吃饭了");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //释放一个许可,意味着当前线程已不再占用资源,其他线程可以继续使用该资源。
        semaphores[(this.philosopherNum)].release();
        semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].release();
        count.release();
    }
}

3 拿起左边筷子和右边筷子不是一个原子性的操作,我们可以加一个全局的锁,让拿左边筷子和右边的筷子变为一个原子操作。

private static Semaphore muntex = new Semaphore(1);
@Override
public void run() {
    try {
        muntex.acquire();
        semaphores[this.philosopherNum].acquire();  //获取锁
        Thread.sleep((long) (1000));   //拿起筷子的时间,会发生死锁
        //acquire(): 获取一个许可。如果信号量的许可已经被其他线程占用,那么当前线程会阻塞,直到有许可可用。
        semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();
        System.out.println(this.getName() + "获取到了筷子" + String.valueOf(this.philosopherNum) +
                           "和" + String.valueOf(this.getRightSemaphoreIndex(this.philosopherNum)) + "开始吃饭了");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //释放一个许可,意味着当前线程已不再占用资源,其他线程可以继续使用该资源。
        semaphores[(this.philosopherNum)].release();
        semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].release();
        muntex.release();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/909027.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mutable用法

mutable 关键字用于允许类的某个成员变量在 const 成员函数中被修改。通常&#xff0c;const 成员函数不能改变对象的任何成员变量&#xff0c;但将成员变量声明为 mutable 可以例外 class Hero { public:Hero():m_Hp(0), m_getHpCounter(0){}int getHp() const {m_getHpCounte…

C++ | Leetcode C++题解之第537题复数乘法

题目&#xff1a; 题解&#xff1a; class Solution { public:string complexNumberMultiply(string num1, string num2) {regex re("\\|i"); vector<string> complex1(sregex_token_iterator(num1.begin(), num1.end(), re, -1), std::sregex_token_iterator…

告别传统营销,HubSpot AI分析工具带你玩转新潮流

你们知道吗&#xff1f;现在的人工智能&#xff08;AI&#xff09;技术可是越来越厉害了&#xff0c;它简直就是我们营销人员的超级外挂&#xff01;有了AI分析工具&#xff0c;我们不仅能优化营销效果&#xff0c;还能大大提升工作效率。那么&#xff0c;具体是怎么一回事呢&a…

Docker打包自己项目推到Docker hub仓库(windows10)

一、启用Hyper-V和容器特性 1.应用和功能 2.点击程序和功能 3.启用或关闭Windows功能 4.开启Hyper-V 和 容器特性 记得重启生效&#xff01;&#xff01;&#xff01; 二、安装WSL2&#xff1a;写文章-CSDN创作中心https://mp.csdn.net/mp_blog/creation/editor/143057041 三…

如何删除react项目的默认图标,使在浏览器中不显示默认图标favicon.ico

要删除 React 项目的默认图标&#xff0c;使在浏览器中不显示默认图标favicon.ico&#xff0c;其实有两种方法&#xff1a; 方法一 方法要点&#xff1a;删除掉 public 目录下的 favicon.ico 文件&#xff0c;再用浏览器访问时&#xff0c;如果加载不到图标文件&#xff0c;就…

软件测试:测试用例详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、通用测试用例八要素   1、用例编号&#xff1b;    2、测试项目&#xff1b;   3、测试标题&#xff1b; 4、重要级别&#xff1b;    5、预置…

支付幂等性的实现中,通过“一锁、二判、三更新”

在这个支付幂等性的实现中&#xff0c;通过“一锁、二判、三更新”严格控制了支付链接生成接口的幂等性&#xff0c;确保同一业务单号在同一时间只会生成一个有效的支付链接&#xff0c;避免重复支付或其他意外操作。 Facade DistributeLock(keyExpression "#payCreate…

Charles抓包_Android

1.下载地址 2.破解方法 3.安卓调试办法 查看官方文档&#xff0c;Android N之后抓包要声明App可用User目录下的CA证书 3.1.在Proxy下进行以下设置&#xff08;路径Proxy->Proxy Settings&#xff09; 3.1.1.不抓包Windows&#xff0c;即不勾选此项&#xff0c;免得打输出不…

Chromium Mojo(IPC)进程通信演示 c++(3)

122版本自带的mojom通信例子channel-associated-interface 仅供学习参考&#xff1a; codelabs\mojo_examples\03-channel-associated-interface-freezing 其余定义参考上一篇文章&#xff1a; Chromium Mojo(IPC)进程通信演示 c&#xff08;2&#xff09;-CSDN博客​​​​…

鸢尾博客项目开源

1.博客介绍 鸢尾博客是一个基于Spring BootVue3 TypeScript ViteJavaFx的客户端和服务器端的博客系统。项目采用前端与后端分离&#xff0c;支持移动端自适应&#xff0c;配有完备的前台和后台管理功能。后端使用Sa-Token进行权限管理,支持动态菜单权限&#xff0c;服务健康…

【模型学习之路】手写+分析bert

手写分析bert 目录 前言 架构 embeddings Bertmodel 预训练任务 MLM NSP Bert 后话 netron可视化 code2flow可视化 fine tuning 前言 Attention is all you need! 读本文前&#xff0c;建议至少看懂【模型学习之路】手写分析Transformer-CSDN博客。 毕竟Bert是tr…

word及Excel常见功能使用

最近一直在整理需规文档及表格&#xff0c;Word及Excel需要熟练使用。 Word文档 清除复制过来的样式 当复制文字时&#xff0c;一般会带着字体样式&#xff0c;此时可选中该文字 并使用 ctrlshiftN 快捷键进行清除。 批注 插入->批注&#xff0c;选中文本 点击“批注”…

【C++篇】数据之林:解读二叉搜索树的优雅结构与运算哲学

文章目录 二叉搜索树详解&#xff1a;基础与基本操作前言第一章&#xff1a;二叉搜索树的概念1.1 二叉搜索树的定义1.1.1 为什么使用二叉搜索树&#xff1f; 第二章&#xff1a;二叉搜索树的性能分析2.1 最佳与最差情况2.1.1 最佳情况2.1.2 最差情况 2.2 平衡树的优势 第三章&a…

【Mac】安装 VMware Fusion Pro

VMware Fusion Pro 软件已经正式免费提供给个人用户使用&#xff01; 1、下载 【官网】 下拉找到 VMware Fusion Pro Download 登陆账号 如果没有账号&#xff0c;点击右上角 LOGIN &#xff0c;选择 REGISTER 注册信息除了邮箱外可随意填写 登陆时&#xff0c;Username为…

文心一言 VS 讯飞星火 VS chatgpt (383)-- 算法导论24.5 3题

三、对引理 24.10 的证明进行改善&#xff0c;使其可以处理最短路径权重为 ∞ ∞ ∞ 和 − ∞ -∞ −∞ 的情况。引理 24.10(三角不等式)的内容是&#xff1a;设 G ( V , E ) G(V,E) G(V,E) 为一个带权重的有向图&#xff0c;其权重函数由 w : E → R w:E→R w:E→R 给出&…

Linux 服务器使用指南:从入门到登录

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; &#x1f6a9;博主致力于用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 目录 一…

【Maven】——基础入门,插件安装、配置和简单使用,Maven如何设置国内源

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 引入&#xff1a; 一&#xff1a;Maven插件的安装 1&#xff1a;环境准备 2&#xff1a;创建项目 二…

数据库基础(2) . 安装MySQL

0.增加右键菜单选项 添加 管理员cmd 到鼠标右键 运行 reg文件 在注册表中添加信息 这样在右键菜单中就有以管理员身份打开命令行的选项了 1.获取安装程序 网址: https://dev.mysql.com/downloads/mysql/ 到官网下载MySQL8 的zip包, 然后解压 下载后的包为: mysql-8.0.16-…

cocos开发QA

目录 TS相关foreach循环中使用return循环延迟动态获取类属性 Cocos相关属性检查器添加Enum属性使用Enum报错 枚举“XXX”用于其声明前实现不规则点击区域使用cc.RevoluteJoint的enable激活组件无效本地存储以及相关问题JSON.stringify(map)返回{}数据加密客户端复制文本使用客户…

flutter区别于vue的写法

View.dart 页面渲染&#xff1a; 类似于vue里面使用 <template> <div> <span> <textarea>等标签绘制页面, flutter 里面则是使用不同的控件来绘制页面 样式 与传统vue不同的是 flutter里面没有css/scss样式表&#xff0c; Flutter的理念是万物皆…