牛郎织女的幸福生活(活锁)

news2024/11/15 12:10:37

从前,有一对夫妻,男的叫牛郎,女的叫织女,他们很好地传承了中华民族的谦让美德,每次吃饭时都会优先考虑对方,如果对方饿的话,就让给对方吃,等对方吃饱了自己才吃,这种美德本身是好的,但是如果一味的谦让,就有可能都吃不上饭…

1. 吃饭逻辑

  • 有两个就餐者,一个是牛郎,一个是织女;
  • 就餐者Diner,有两个属性,一个是就餐人名字 name,一个是是否饥饿isHungry
  • 就餐者有个行为方法eatWith(),他需要两个参数,一个是勺子,一个是对方,方法里面描述的是吃饭的事宜。
  • 定义一个勺子类,有勺子表示就能够吃饭,里面有个属性表示所属人owner

2. 代码实现

(1)两个就餐者开启两个线程

public class LiveLock {

    public static void main(String[] args) {
        Diner husband = new Diner("牛郎");
        Diner wife = new Diner("织女");

        Spoon spoon = new Spoon(husband);

        new Thread(new Runnable() {
            @Override
            public void run() {
                husband.eatWith(spoon, wife);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                wife.eatWith(spoon, husband);
            }
        }).start();
    }
}

(2) 定义就餐者 Diner 类

static class Diner {

    /**
     * 吃饭的人
     */
    private String name;
    /**
     * 是否饥饿
     */
    private boolean isHungry;

    public Diner(String name) {
        this.name = name;
        isHungry = true;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isHungry() {
        return isHungry;
    }

    public void setHungry(boolean hungry) {
        isHungry = hungry;
    }
}

(3) 定义行为方法eatWith()

    /**
     * 方法里面描述的是吃饭的事
     *
     * @param spoon 勺子
     * @param spouse 夫妻对方
     */
    public void eatWith(Spoon spoon, Diner spouse) {
        while (isHungry) {
            //如果勺子不是自己的,则等一会儿,等对方吃完
            if (spoon.getOwner() != this) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                continue;
            }
            //先检查对方是否饥饿,如果对方饥饿的话先把勺子交给对方,让对方先吃
            if (spouse.isHungry) {
                System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧");
                spoon.setOwner(spouse);
                continue;
            }

            //开始使用勺子吃饭
            spoon.use();
            //吃完后把自己的状态改成非饥饿状态
            isHungry = false;
            System.out.println(name + ": 我吃完了");
            //同时把勺子直接给到对方
            spoon.setOwner(spouse);
        }
    }

(4) 定义一个勺子类

    static class Spoon {
    
        /** 所属人owner **/
        private Diner owner;

        public Spoon(Diner owner) {
            this.owner = owner;
        }

        public Diner getOwner() {
            return owner;
        }

        public void setOwner(Diner owner) {
            this.owner = owner;
        }

        public synchronized void use() {
            System.out.printf("%s吃完了!", owner.name);


        }
    }

3. 代码运行结果

完整代码如下:

public class LiveLock {

    public static void main(String[] args) {
        Diner husband = new Diner("牛郎");
        Diner wife = new Diner("织女");

        Spoon spoon = new Spoon(husband);

        new Thread(new Runnable() {
            @Override
            public void run() {
                husband.eatWith(spoon, wife);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                wife.eatWith(spoon, husband);
            }
        }).start();
    }
    
    static class Diner {

        /**
         * 吃饭的人
         */
        private String name;
        /**
         * 是否饥饿
         */
        private boolean isHungry;

        /**
         * 方法里面描述的是吃饭的事
         *
         * @param spoon 勺子
         * @param spouse 夫妻对方
         */
        public void eatWith(Spoon spoon, Diner spouse) {
            while (isHungry) {
                //如果勺子不是自己的,则等一会儿,等对方吃完
                if (spoon.getOwner() != this) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                //先检查对方是否饥饿,如果对方饥饿的话先把勺子交给对方,让对方先吃
                if (spouse.isHungry) {
                    System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧");
                    spoon.setOwner(spouse);
                    continue;
                }

                //开始使用勺子吃饭
                spoon.use();
                //吃完后把自己的状态改成非饥饿状态
                isHungry = false;
                System.out.println(name + ": 我吃完了");
                //同时把勺子直接给到对方
                spoon.setOwner(spouse);
            }
        }
        public Diner(String name) {
            this.name = name;
            isHungry = true;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public boolean isHungry() {
            return isHungry;
        }

        public void setHungry(boolean hungry) {
            isHungry = hungry;
        }
    }
    static class Spoon {
    
        /** 所属人owner **/
        private Diner owner;

        public Spoon(Diner owner) {
            this.owner = owner;
        }

        public Diner getOwner() {
            return owner;
        }

        public void setOwner(Diner owner) {
            this.owner = owner;
        }

        public synchronized void use() {
            System.out.printf("%s吃完了!", owner.name);


        }
    }
}

打印结果:

我们可以看到,程序一直重复让对方先吃,这就陷入了活锁的状态,因为程序并没有停下,还在不停打印输出,但程序一直在原地打转,没有实际前进,没有完成吃饭的动作。

5. 出现原因

其实主要问题出现在 if (spouse.isHungry) 这一句上,只要检测到对方是饥饿的话就始终谦让!

  • 原因:重试机制不变,吃饭始终谦让
  • 可以借鉴以太网的指数退避算法:连接重试时间不是固定的,而是随机的,并且随机范围随着碰撞强度的升高而逐渐扩大
  • 处理方案:加入随机因素,允许某些时候不谦让

6. 解决活锁问题

我们只要在 if (spouse.isHungry) 这一句的判断条件加一个随机因素 (random.nextInt(10) < 9) 即可:

/**
     * 方法里面描述的是吃饭的事
     *
     * @param spoon 勺子
     * @param spouse 夫妻对方
     */
    public void eatWith(Spoon spoon, Diner spouse) {
        while (isHungry) {
            //如果勺子不是自己的,则等一会儿,等对方吃完
            if (spoon.getOwner() != this) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                continue;
            }
            Random random = new Random();
            //先检查对方是否饥饿,如果对方饥饿的话先把勺子交给对方,让对方先吃,random.nextInt(10) < 9 表示有十分之一的概率不谦让
            if (spouse.isHungry && random.nextInt(10) < 9) {
                System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧");
                spoon.setOwner(spouse);
                continue;
            }

            //开始使用勺子吃饭
            spoon.use();
            //吃完后把自己的状态改成非饥饿状态
            isHungry = false;
            System.out.println(name + ": 我吃完了");
            //同时把勺子直接给到对方
            spoon.setOwner(spouse);
        }
    }

打印结果:

可以看到他们在谦让了多次之后,织女终于吃到饭了,吃饱了之后勺子给了牛郎,牛郎也吃完了,然后就过上了没羞没臊的幸福生活…

文章来源:牛郎织女的幸福生活(活锁)

个人微信:CaiBaoDeCai

微信公众号名称:Java知者

微信公众号 ID: JavaZhiZhe

谢谢关注!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot整合OSS文件上传

一、注册阿里云账号并开通OSS服务 1、登录阿里云账号 2、创建一个bucket 3、创建子用户 对自用户分配权限&#xff0c;打开操作OSS的全部权限&#xff08;也可根据业务需求进行更改&#xff09; 4、配置上传跨域规则 任何来源: *允许方法: POST任何请求头Headers: * 二、…

pytorch实现图像分类任务-手写数字识别(一)

Pytorch手写数字识别 Minst数据集介绍 Size: 2828 灰度手写数字图像 Num: 训练集 60000 和 测试集 10000&#xff0c;一共70000张图片 Classes: 0&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#xff0c;7&#xff0c;8&#xff0c;9 一…

pybullet学习(一)——安装与入门pybullet

PyBullet介绍 PyBullet 基于著名的开源物理引擎 bullet 开发&#xff0c;封装成了 Python 的一个模块&#xff0c;用于机器人仿真和学习。PyBullet 支持加载 URDF、SDF、MJCF 等多种机器人描述文件&#xff0c;并提供正/逆向运动学、正/逆向动力学、碰撞检测、射线相交查询等功…

FreeRTOS多任务系统

FreeRTOS 文章目录 FreeRTOS1 单任务和多任务系统1.1 单任务系统1.2 多任务系统 2 FreeRTOS 任务状态3 FreeRTOS 任务优先级4 Free RTOS 任务调度方式4.1 抢占式调度4.2 时间片调度 5 FreeRTOS 任务控制块6 FreeRTOS 任务栈 1 单任务和多任务系统 1.1 单任务系统 单任务系统的…

手把手教你搭建网站(零基础,不用写代码)

没有前言直接开始正文&#xff0c;搭建一个博客需要服务器&#xff0c;域名&#xff0c;博客程序。 博客程序常用的有wordpress&#xff0c;z-blog&#xff0c;typecho等等&#xff0c;其中wordpress和z-blog最为简单&#xff0c;typecho需要一定的技术含量&#xff0c;这里暂…

17.4:图的拓扑排序

拓扑序一定是有向无环图。 拓扑排序不唯一 拓扑排序方法一&#xff1a; 利用入度为零进行排序 思路&#xff1a;谁的入度为0&#xff0c;谁就是开始节点&#xff0c;将开始节点打印完之后&#xff0c;将开始节点的直接邻居的入度减1&#xff0c;然后重复。 package algorith…

【软件测试】稳定性和可靠性测试在软件开发中的重要性

软件测试的某些方面经常会在那些刚接触流程的人中造成混淆——例如在稳定性和可靠性测试之间划清界限。 两者通常可以互换使用&#xff0c;并且有一个共同的目标&#xff0c;即确保系统可以在选定的时间范围内稳定运行。 在这篇文章中&#xff0c;我们将仔细研究什么是稳定性测…

10年Java开发,准备去腾讯了~

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;又得准备面试了&#xff0c;不知道从何下手&#xff01; 不论是跳槽涨薪&#xff0c;还是学习提升&#xff01;先给自己定一个小目标&#xff0c;然后再朝着目标去努力就完事儿了&#xff01; 为了帮大家节约时间&a…

chatgpt赋能python:Python分词,助力文本处理和搜索引擎优化

Python分词&#xff0c;助力文本处理和搜索引擎优化 作为一种广泛应用于文本处理的编程语言&#xff0c;Python在分词处理方面也有着得天独厚的优势。Python分词不仅可以帮助我们完成文本处理任务&#xff0c;还能够为搜索引擎优化提供便利。 什么是分词&#xff1f; 分词&a…

集线器与交换机的区别

1.集线器与交换机的区别 笔记来源&#xff1a; 湖科大教书匠&#xff1a;集线器与交换机的区别 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 1.1 集线器 早期总线型以太网的机械连接点不太可靠 使用双绞线和集线器相对可靠 集线器可以在物理层…

计算机网络和因特网概述

一.计算机网络和因特网 计算机网络是指多台计算机通过通信线路或其他连接方式相互连接起来&#xff0c;实现信息交换和共享的系统。而因特网是利用标准化协议互联起来的全球性计算机网络&#xff0c;是计算机网络的一种特殊形式。 1.1 什么是因特网 1.1.1 从具体构成描述 因…

day04——特征处理之特征降维

特征处理之特征降维 一、特征选择Filter(过滤式)1&#xff0c;低方差特征过滤2、相关系数 Embedded (嵌入式) 二、主成分分析&#xff08;PCA&#xff09; 特征降维&#xff1a;如果特征本身存在问题或者特征之间相关性较强&#xff0c;对于算法学习预测会影响较大。降维是指在…

设计模式-解释器模式

解释器模式 问题背景解释器模式基本介绍原理类图 使用解释器模式来解决问题UML类图代码示例运行结果 注意事项和细节 问题背景 通过解释器模式来实现一个表达式的加减运算 例如&#xff1a;在我们先输入一个表达式&#xff1a;abc-de&#xff0c;然后输入a&#xff0c;b&#…

layui框架实战案例(22):layui-tab-title的宽度自适应的解决方案

HTML源代码 <div class"layui-card"><div class"layui-card-header">卡片风格</div><div class"layui-card-body"><div class"layui-tab layui-tab-card"><ul class"layui-tab-title">…

酷游SVG-地址路径

说到SVG中最复杂的东西&#xff0c;路径(Path)绝对是一个大魔王&#xff0c;它有太多的commands可以用来定义路径。【娜娜提供酷游地kw9㍠neт地址】透过Path虽然可以绘制效果不错的SVG图形&#xff0c;但是要自己定义一个个坐标点&#xff0c;再去把它们完美的串连在一起&…

《银行从业法律法规》第二章——金融基础知识

第二章 金融基础知识 第二节 货币政策 【 知识点1】 货币政策目标 制定和实施货币政策&#xff0c; 首先必须明确货币政策最终要达到的目的&#xff0c; 即货币政策的最终目标。中央银行通过货币政策工具操作直接引起操作目标的变动&#xff0c;操作目标的变动又通过一定的途…

libarchive 如何在Ubuntu中进行安装

简介 libarchive是一个开源的压缩和归档库。 它支持实时访问多种压缩文件格式&#xff0c;比如7z、zip、cpio、pax、rar、cab、uuencode等&#xff0c;因此应用十分广泛。 举个例子&#xff0c;我写了一段代码&#xff0c;想要解压一个压缩包&#xff0c;那我就得 fork 一个 …

python制作简单版天天酷跑,是不是你平日里摸鱼小游戏呀

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 天天酷跑是一款轻松好玩、简单时尚的跑酷类手机游戏。 那我们能不能用python模拟出这个一个游戏呢&#xff1f; 答案当然是可以的&#xff0c;今天我就给大家带来简易版的天天酷跑小游戏 开发环境&#xff1a; 版 本&a…

如何用Python写个网页爬取程序

如何用Python写个网页爬取程序 准备开发工具安装PythonPython安装pipPip安装爬取插件准备好网页地址代码实现 准备开发工具 额&#xff0c;作者用的是vscode。具体怎么安装自行百度哈&#xff0c;这个都不会建议就不要学爬取了。 不忍心藏着也&#xff0c;给你个方法吧 vsc…

2023年第六届广西大学生程序设计竞赛(热身赛)题解

题目均来自去年的省赛原题 参考资料 知乎&#xff1a;第五届GXCPC广西大学生程序设计竞赛 部分题解&#xff08;无CDK&#xff09; A题送分题&#xff0c;跳过 B 位运算lowbit函数 题目大意&#xff1a; 对一个数&#xff08;二进制&#xff09;进行操作&#xff0c;询问使其…