使用阻塞队列解决生产者-消费者问题

简介:

如果你想避免使用错综复杂的waitnotify的语句,BlockingQueue非常有用。BlockingQueue可用于解决生产者-消费者问题,如下代码示例。对于每个开发人员来说,生产者消费者问题已经非常熟悉了,这里我将不做详细描述。

为什么BlockingQueue适合解决生产者消费者问题

任何有效的生产者-消费者问题解决方案都是通过控制生产者put()方法(生产资源)和消费者take()方法(消费资源)的调用来实现的,一旦你实现了对方法的阻塞控制,那么你将解决该问题。

Java通过BlockingQueue提供了开箱即用的支持来控制这些方法的调用(一个线程创建资源,另一个消费资源)。java.util.concurrent包下的BlockingQueue接口是一个线程安全的可用于存取对象的队列。

BlockingQueue是一种数据结构,支持一个线程往里存资源,另一个线程从里取资源。这正是解决生产者消费者问题所需要的,那么让我们开始解决该问题吧。

使用BlockingQueue解决生产者消费者问题

生产者

以下代码用于生产者线程

复制代码
package cn.edu.hdu.chenpi.cpdemo.howtodoinjava;

import java.util.concurrent.BlockingQueue;

class Producer implements Runnable {
    protected BlockingQueue<Object> queue;

    Producer(BlockingQueue<Object> theQueue) {
        this.queue = theQueue;
    }

    public void run() {
        try {
            while (true) {
                Object justProduced = getResource();
                queue.put(justProduced);
                System.out.println("Produced resource - Queue size now = "
                        + queue.size());
            }
        } catch (InterruptedException ex) {
            System.out.println("Producer INTERRUPTED");
        }
    }

    Object getResource() {
        try {
            Thread.sleep(100); // simulate time passing during read
        } catch (InterruptedException ex) {
            System.out.println("Producer Read INTERRUPTED");
        }
        return new Object();
    }
}
复制代码

这里,生产者线程创建一个资源(Object对象)并将它存入队列中,如果队列已经满了(最大为20),它将会等待直到消费者线程从队列中取走资源,所以队列的长度永远不会超过最大值,比如20。

消费者

以下代码用于消费者线程

复制代码
package cn.edu.hdu.chenpi.cpdemo.howtodoinjava;

import java.util.concurrent.BlockingQueue;

class Consumer implements Runnable {
    protected BlockingQueue<Object> queue;

    Consumer(BlockingQueue<Object> theQueue) {
        this.queue = theQueue;
    }

    public void run() {
        try {
            while (true) {
                Object obj = queue.take();
                System.out.println("Consumed resource - Queue size now = "
                        + queue.size());
                take(obj);
            }
        } catch (InterruptedException ex) {
            System.out.println("CONSUMER INTERRUPTED");
        }
    }

    void take(Object obj) {
        try {
            Thread.sleep(100); // simulate time passing
        } catch (InterruptedException ex) {
            System.out.println("Consumer Read INTERRUPTED");
        }
        System.out.println("Consuming object " + obj);
    }
}
复制代码

队列中有资源的话,消费者线程将从队列中取走资源,否则会等待直到生产者创建资源。

测试该解决方案是否运行正常

现在,让我们测试下以上生产者消费者线程是否能正常工作:

复制代码
package cn.edu.hdu.chenpi.cpdemo.howtodoinjava;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class ProducerConsumerExample {
    public static void main(String[] args) throws InterruptedException {
        int numProducers = 4;
        int numConsumers = 3;

        BlockingQueue<Object> myQueue = new LinkedBlockingQueue<>(20);

        for (int i = 0; i < numProducers; i++) {
            new Thread(new Producer(myQueue)).start();
        }

        for (int i = 0; i < numConsumers; i++) {
            new Thread(new Consumer(myQueue)).start();
        }

        // Let the simulation run for, say, 10 seconds
        Thread.sleep(10 * 1000);

        // End of simulation - shut down gracefully
        System.exit(0);
    }
}
复制代码

当我们运行以上代码,会发现类似如下输出结果:

Consumed resource - Queue size now = 1
Produced resource - Queue size now = 1
Consumed resource - Queue size now = 1
Consumed resource - Queue size now = 1
Produced resource - Queue size now = 1
Produced resource - Queue size now = 1
Produced resource - Queue size now = 1
Consuming object java.lang.Object@14c7f728
Consumed resource - Queue size now = 0
Consuming object java.lang.Object@2b71e323
Consumed resource - Queue size now = 0
Produced resource - Queue size now = 0
Produced resource - Queue size now = 1
Produced resource - Queue size now = 2
Consuming object java.lang.Object@206dc00b
Consumed resource - Queue size now = 1
Produced resource - Queue size now = 2
Produced resource - Queue size now = 3
Consuming object java.lang.Object@1a000bc0
Consumed resource - Queue size now = 2
Consuming object java.lang.Object@25b6183d
Consumed resource - Queue size now = 1
Produced resource - Queue size now = 2
Produced resource - Queue size now = 3
...
...
Produced resource - Queue size now = 20
Consuming object java.lang.Object@2b3cd3a6
Consumed resource - Queue size now = 19
Produced resource - Queue size now = 20
Consuming object java.lang.Object@3876982d
Consumed resource - Queue size now = 19
Produced resource - Queue size now = 20

从输出结果中,我们可以发现队列大小永远不会超过20,消费者线程消费了生产者生产的资源。

 


本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5553325.html,如需转载请自行联系原作者

相关文章
|
机器学习/深度学习 人工智能 前端开发
机器学习PAI常见问题之web ui 项目启动后页面打不开如何解决
PAI(平台为智能,Platform for Artificial Intelligence)是阿里云提供的一个全面的人工智能开发平台,旨在为开发者提供机器学习、深度学习等人工智能技术的模型训练、优化和部署服务。以下是PAI平台使用中的一些常见问题及其答案汇总,帮助用户解决在使用过程中遇到的问题。
|
jenkins 持续交付 开发工具
Jenkins-pipline流水线语法介绍并结合Blue Ocean查看流水线(十四)
jenkins集成pipeline流水线 1.pipeline概述 pipeline流水线,可以直观的展示每个阶段做的任务,以及每个阶段耗费的时间。 pipeline不在使用鼠标来实现自动构建,也不要去看控制台日志,而是全程使用代码的方式来实现,构建完成后会展示一个视图,用来展示每个阶段完成的情况
556 0
Jenkins-pipline流水线语法介绍并结合Blue Ocean查看流水线(十四)
|
10月前
|
安全 网络安全 数据安全/隐私保护
利用Docker的网络安全功能来保护容器化应用
通过综合运用这些 Docker 网络安全功能和策略,可以有效地保护容器化应用,降低安全风险,确保应用在安全的环境中运行。同时,随着安全威胁的不断变化,还需要持续关注和研究新的网络安全技术和方法,不断完善和强化网络安全保护措施,以适应日益复杂的安全挑战。
281 61
|
XML Java Android开发
Android spinner 的使用
Android spinner 的使用
260 6
|
11月前
|
PyTorch 算法框架/工具 计算机视觉
目标检测实战(二):YoloV4-Tiny训练、测试、评估完整步骤
本文介绍了使用YOLOv4-Tiny进行目标检测的完整流程,包括模型介绍、代码下载、数据集处理、网络训练、预测和评估。
656 2
目标检测实战(二):YoloV4-Tiny训练、测试、评估完整步骤
|
11月前
|
机器学习/深度学习 人工智能 自然语言处理
前端大模型入门(三):编码(Tokenizer)和嵌入(Embedding)解析 - llm的输入
本文介绍了大规模语言模型(LLM)中的两个核心概念:Tokenizer和Embedding。Tokenizer将文本转换为模型可处理的数字ID,而Embedding则将这些ID转化为能捕捉语义关系的稠密向量。文章通过具体示例和代码展示了两者的实现方法,帮助读者理解其基本原理和应用场景。
2816 1
|
运维 负载均衡 算法
“分布式基础概念”全面解析,让你秒懂分布式系统!【一】
该博客文章全面解析了分布式系统的基础概念,包括微服务架构、集群与分布式的区别、节点定义、远程调用、负载均衡、服务注册与发现、配置中心、服务熔断与降级以及API网关,帮助读者快速理解分布式系统的关键组成部分和工作原理。
“分布式基础概念”全面解析,让你秒懂分布式系统!【一】
|
弹性计算 Windows
震惊!ECS Windows 系统磁盘竟“撒谎”,空间去哪儿了?别急,这里有终极破解法!
【8月更文挑战第15天】在使用ECS Windows系统时,可能会遇到磁盘显示占用的空间远超实际文件大小的情况,导致空间不足。原因包括系统还原点、卷影副本累积及回收站文件未彻底删除等。解决方法有:清除系统还原点(`vssadmin delete shadows /all`),清空回收站,删除临时文件夹中的文件,以及检查并修复磁盘错误。这些步骤能有效释放空间,保证系统稳定运行。
263 4
|
Unix Shell 测试技术
误删数据恢复系列1
误删数据恢复系列1
229 0
|
编译器 Linux C语言
交叉编译环境的安装
交叉编译环境的安装
206 0