【生产者消费者模型的 Java 实现】

news2024/10/1 7:29:42

文章目录

  • 前言
  • 传统派
  • 维新派


前言

题目:一个初始值为零的变量,多个线程对其交替操作,分别加1减1

实现步骤:

  1. 线程操作资源类
  2. 判断,干活,通知
  3. 防止虚假唤醒机制,即:多线程的判断需要用 while,不能使用 if(jdk 要求的,保证线程不会出错)

传统派

加锁实现,通过加锁保证线程的安全

class ShareData {// 资源类

    private int number = 0;
    // 锁
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void increment() throws InterruptedException {
        lock.lock();

        try {
            // 1. 判断,不能使用 if
//            if (number != 0) {
            while (number != 0) {
                // 有值,等待消费,不能生产
                condition.await();
            }
            // 2. 干活
            number++;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            // 3. 通知,唤醒
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
        // 手动解锁
            lock.unlock();
        }
    }

    public void decrement() {
        lock.lock();
        try {
            // 1. 判断
//            if (number == 0) {
            while (number == 0) {
                // 有值,等待消费,不能生产
                condition.await();
            }
            // 2. 干活
            number--;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            // 3. 通知,唤醒
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}


public class Test {
    public static void main(String[] args) throws Exception {
        // 传统

        ShareData shareData = new ShareData();

        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    shareData.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Produce0").start();


        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                shareData.decrement();
            }
        }, "Consume0").start();


        // 多个线程验证性质 3
        // 发现不是生产一个就消费一个了
        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    shareData.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Produce1").start();


        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                shareData.decrement();
            }
        }, "Consume2").start();


    }
}

输出结果:

在这里插入图片描述


维新派

使用阻塞队列和原子类实现


class MyResource {
    // 默认开启
    private volatile boolean FLAG = true;
    // 原子类作为共享变量
    private final AtomicInteger atomicInteger = new AtomicInteger();
	// 阻塞队列实现锁的功能
    BlockingQueue<String> blockingQueue = null;

    public MyResource(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    public void myProd() throws InterruptedException {
        String date = null;
        boolean retValue;
        while (FLAG) {
            date = atomicInteger.incrementAndGet() + "";
           retValue =  blockingQueue.offer(date, 2L, TimeUnit.SECONDS);
            if (retValue) {
                System.out.println(Thread.currentThread().getName() + "\t插入队列" + date + "成功");
            } else {
                System.out.println(Thread.currentThread().getName() + "\t插入队列" + date + "失败");
            }
            // 一秒生产一个
            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println(Thread.currentThread().getName() + "\t停止生产");
    }

    public void myConsumer() throws InterruptedException {
        String result = null;
        while (FLAG) {
            result = blockingQueue.poll(2L, TimeUnit.SECONDS);
            if (result == null || result.equalsIgnoreCase("")) {
                FLAG = false;
                System.out.println(Thread.currentThread().getName() + "\t超过两秒没有取到,退出");
                System.out.println();
                System.out.println();
                return;
            }

            System.out.println(Thread.currentThread().getName() + "\t消费队列" + result + "成功");

        }
    }

    public void stop() {
        this.FLAG = false;
    }
}


public class Test {
    public static void main(String[] args) throws Exception {
        MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t生产线程启动");
            try {
                myResource.myProd();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Prod").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t消费线程启动");
            try {
                myResource.myConsumer();
                System.out.println();
                System.out.println();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Consumer").start();


        Thread.sleep(5000);

        System.out.println();

        System.out.println("时间到,main 线程叫停");
        myResource.stop();
    }
}

输出结果:

在这里插入图片描述

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

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

相关文章

自旋框的使用

1. 自旋框 实例化 //实例化单精度自旋框QSpinBox* spinBox new QSpinBox(this);//实例化双精度自旋框QDoubleSpinBox* doubleSpinBox new QDoubleSpinBox(this);1.1 单精度自旋框 QSpinBox 1.1.1 单精度自旋框的基本函数 QSpinBox_QDoubleSpinBox Dialog.cpp #include "…

更快更稳的4K响应鼠标,小手玩家也能用,雷柏VT9PRO mini

雷柏今年推出了不少新品&#xff0c;特别是一系列支持4K回报率的鼠标&#xff0c;凭借敏捷的响应速度&#xff0c;获得了非常好的评价。不过之前雷柏出的4K鼠标都多适合中大手&#xff0c;对小手用户不友好&#xff0c;而且配色较少&#xff0c;都是黑白色的基础款&#xff0c;…

基于爬虫和Kettle的书籍信息采集与预处理

一&#xff1a;爬虫 1、爬取的目标 将读书网上的书籍的基本信息&#xff0c;比如&#xff1a;封面、书名、作者、出版社、价格、出版时间、内容简介、作者简介、书籍目录、ISBN和标签爬取出来&#xff0c;并将爬取的结果放入数据库中&#xff0c;方便存储。 2、网站结构 图1读…

HackTheBox - Medium - Linux - UpDown

UpDown UpDown 是一台中等难度的 Linux 机器&#xff0c;暴露了 SSH 和 Apache 服务器。在Apache服务器上&#xff0c;有一个Web应用程序&#xff0c;允许用户检查网页是否已启动。服务器上标识了一个名为“.git”的目录&#xff0c;可以下载以显示目标上运行的“dev”子域的源…

从事铁路工作保护足部,穿什么劳保鞋更安全

铁路运输在我国交通运输业中起着骨干作用&#xff0c;为国民经济的可持续发展和人口流动做出了巨大贡献。安全是铁路运输不可忽视的问题&#xff0c;在作业场地随处能见到“安全就是生命&#xff0c;责任重于泰山”的安全标语&#xff0c;由此可见安全问题是放在首位的。 铁路施…

❤ Vue3 完整项目太白搭建 Vue3+Pinia+Vant3/ElementPlus+typerscript(一)yarn 版本控制 ltb (太白)

❤ 项目搭建 一、项目信息 Vue3 完整项目搭建 Vue3PiniaVant3/ElementPlustyperscript&#xff08;一&#xff09;yarn 版本控制 项目地址&#xff1a; 二、项目搭建 &#xff08;1&#xff09;创建项目 yarn create vite <ProjectName> --template vueyarn install …

用我这套模板,几分钟做出文档网站!

大家好&#xff0c;我是保姆皮&#xff0c;最近我上线了自己的《编程宝典》网站&#xff0c;可以在线阅读我分享过的各种编程学习路线和知识干货。 指路&#xff1a;https://codefather.cn/ 不少小伙伴催我出教程&#xff0c;说也想做个类似的文档网站。 所以我用最快的速度出了…

挑选富集分析结果 enrichments

#2.2挑选term---selected_clusterenrichenrichmets[grepl(pattern "cilium|matrix|excular|BMP|inflamm|development|muscle|vaso|pulmonary|alveoli",x enrichmets$Description),]head(selected_clusterenrich) distinct(selected_clusterenrich)# remove duplica…

firewalld防火墙命令行工具

firewall-cmd命令 &#xff08;1&#xff09;启动、停止、查看firewalld服务 在安装CentOS 7系统时&#xff0c;会自动安装firewalld 和图形化工具firewall-config.执行以下命令可 以启动 firewalld 并设置为开机自启动状态。 [rootllcgc ~]# systemctl start firewalld.serv…

关于Python里xlwings库对Excel表格的操作(三十一)

这篇小笔记主要记录如何【如何使用“Chart类”、“Api类"和“Axes函数”设置绘图区外框线型、颜色、粗细及填充颜色】。前面的小笔记已整理成目录&#xff0c;可点链接去目录寻找所需更方便。 【目录部分内容如下】【点击此处可进入目录】 &#xff08;1&#xff09;如何安…

Unity中URP下实现能量罩(扭曲流光花纹)

文章目录 前言一、能量罩花纹1、在属性面板接收能量罩花纹纹理2、申明 纹理 和 采样器3、在顶点着色器,应用 Tilling 和 Offset4、在片元着色器,纹理采样后,与之前的结果相乘输出二、能量罩流光1、在顶点着色器,记录原uv值2、在片元着色器,使用 uv 的 y 值,乘以一个系数 …

Linux的DNS域名解析服务

一.DNS基础 1.1 DNS简介 DNS域名系统 &#xff08;Domain Name System 缩写为&#xff1a;DNS&#xff09;是因特网的一项核心服务&#xff0c;它作为可以将 域名 和 IP地址 相互映射的一个分布式数据库&#xff0c;能够使人更加方便的访问互联网&#xff0c;而不用去记住能够…

企业数据中台整体介绍及建设方案:文件全文51页,附下载

关键词&#xff1a;数据中台解决方案&#xff0c;数据治理&#xff0c;数据中台技术架构&#xff0c;数据中台建设内容&#xff0c;数据中台核心价值 一、什么是数据中台&#xff1f; 数据中台是指通过数据技术&#xff0c;对海量数据进行采集、计算、存储、加工&#xff0c;…

DNS域名解析以及操作流程

dns:将域名转化为IP地址的过程&#xff0c;域名方便人们记忆&#xff0c;ip地址过长&#xff0c;且都是数字&#xff0c;不方便记忆&#xff0c;所以才出现了域名。 怎么实现访问域名等于访问ip地址 1.老方法&#xff1a;写入文件里 /etc/hosts 左边 IP地址 右边域名 格式例…

XSS的利用(包含:蓝莲花、beef-xss)

0x00、环境搭建 dvwa靶场 操作指南和最佳实践:使用 DVWA 了解如何防止网站漏洞_dvwa源代码-CSDN博客 xss漏洞接收平台 下载:GitHub - firesunCN/BlueLotus_XSSReceiver 将解压后的BlueLotus_XSSReceiver原代码放置 phpstudy 安装目录的WWW文件夹下 访问平台:http://127…

N-137基于springboot,vue运动会报名管理系统

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vueAvueElementUI 服务端技术&#xff1a;springbootmybatis 本项…

XCTF:MISCall[WriteUP]

使用file命令&#xff0c;查看该文件类型 file d02f31b893164d56b7a8e5edb47d9be5 文件类型&#xff1a;bzip2 使用bzip2命令可对该文件进行解压 bzip2 -d d02f31b893164d56b7a8e5edb47d9be5 生成了一个后缀为.out的文件 再次使用file命令&#xff0c;查看该文件类型 file…

WARNING: IPv4 forwarding is disabled. Networking will not work.

今天用docker部署容器&#xff0c;发现一个问题&#xff0c;docker-compose up启动成功&#xff0c;但无法正常访问接口。 查找问题步骤&#xff1a; 1、直接在服务器运行jar包&#xff0c;发现可以正常启动&#xff0c;也能正常访问接口&#xff0c;排除jar包本身问题以及防…

Google推出Telecom Jetpack库,让Android通话应用创建更简单

Google推出Telecom Jetpack库&#xff0c;让Android通话应用创建更简单 Telecom Jetpack库的最新Alpha版本已经推出。该库提供了多个API&#xff0c;以简化Android开发者创建语音和/或视频通话应用程序的过程&#xff0c;支持常见功能&#xff0c;例如接听/拒绝、音频路由等等…

在机械行业中,直线导轨和弧形导轨哪个应用范围更广泛?

弧形导轨和直线导轨是两种常见的导轨类型&#xff0c;直线导轨主要被用于高精度或快速直线往复运动场所&#xff0c;而弧形导轨是一种专门设计用于曲线运动的导轨系统&#xff0c;那么在机械行业中&#xff0c;直线导轨和弧形导轨哪个应用范围更加广泛呢&#xff1f; 直线导轨主…