Java多线程之生产者消费者模式详解

 更新时间:2022年03月01日 16:15:15   作者:小小茶花女  
这篇文章主要为大家详细介绍了Java多线程之生产者消费者模式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

问题:

1.什么是阻塞队列?如何使用阻塞队列来实现生产者-消费者模型?

2. 生产者消费者模型的作用是什么?

1. 生产者消费者模型

在生产者-消费者模式中,通常有两类线程,即生产者线程(若干个)和消费者线程(若干个)。生产者线程向消息队列加入数据,消费者线程则从消息队列消耗数据。生产者和消费者、消息队列之间的关系结构图如图:

在这里插入图片描述

(1) 消息队列可以用来平衡生产和消费的线程资源;

(2) 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据 ;

(3) 消息队列是有容量限制的,消息队列满后,生产者不能再加入数据;消息队列空时,消费者不能再取出数据;

(4) 消息队列是线程安全的,在并发操作消息队列的过程中,不能出现数据不一致的情况;或者在多个线程并发更改共享数据后,不会造成出现脏数据的情况;

(5) JDK 中各种阻塞队列,采用的就是这种模式;

2. 实现生产者消费者模型

1、消息队列中存放的消息类:

/**
 * 消息队列中存放的消息类
 */
final public class Message {
    private int id;
    private int value;
    public Message(int id,int value){
        this.id = id;
        this.value = value;
    }
    public int getId() {
        return id;
    }
    public int getValue() {
        return value;
    }
}

2、实现阻塞队列(消息队列) :

import lombok.extern.slf4j.Slf4j;
import java.util.LinkedList;
/**
 * 实现一个阻塞队列(消息队列),实现java线程间通信
 */
@Slf4j
public class MessageQueue {
    // 消息队列的容量
    private int capacity;
    // 消息队列
    LinkedList<Message> messageQueue = new LinkedList<>();
    // 设置消息队列的容量
    public MessageQueue(int capacity){
        this.capacity = capacity;
    }
    // 从消息队列中取消息
    public Message take(){
        synchronized (messageQueue){
            // 如果消息队列为空
            while (messageQueue.isEmpty()){
                try {
                    log.debug("队列为空, 消费者线程等待");
                    messageQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Message message = messageQueue.removeFirst();
            log.debug("已消费消息 {}", message);
            // 走到这,说明消息队列不为null
            messageQueue.notifyAll();
            return message;
        }
    }
    // 往消息队列中放消息
    public void put(Message message){
        synchronized (messageQueue){
            // 如果消息队列已满
            while (messageQueue.size()==capacity){
                try {
                    log.debug("队列已满, 生产者线程等待");
                    messageQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            messageQueue.addLast(message);
            log.debug("已生产消息 {}", message);
            // 走到这,说明消息队列不满
            messageQueue.notifyAll();
        }
    }
}

3、测试:

public class Main {
    public static void main(String[] args) {
        MessageQueue queue = new MessageQueue(2);
        for(int i=0;i<3;i++){
            int id = i;
            new Thread(()->{
                queue.put(new Message(id,id));
            },"生产者").start();
        }
        new Thread(()->{
            while (true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message message = queue.take();
            }
        },"消费者").start();
    }
}

执行结果:

15:31:28.488 [生产者] DEBUG com.example.test.MessageQueue - 已生产消息 com.example.test.Message@54309a75
15:31:28.507 [生产者] DEBUG com.example.test.MessageQueue - 已生产消息 com.example.test.Message@50915389
15:31:28.507 [生产者] DEBUG com.example.test.MessageQueue - 队列已满, 生产者线程等待
15:31:29.486 [消费者] DEBUG com.example.test.MessageQueue - 已消费消息 com.example.test.Message@54309a75
15:31:29.486 [生产者] DEBUG com.example.test.MessageQueue - 已生产消息 com.example.test.Message@6340ac12
15:31:30.487 [消费者] DEBUG com.example.test.MessageQueue - 已消费消息 com.example.test.Message@50915389
15:31:31.487 [消费者] DEBUG com.example.test.MessageQueue - 已消费消息 com.example.test.Message@6340ac12
15:31:32.488 [消费者] DEBUG com.example.test.MessageQueue - 队列为空, 消费者线程等待

3. 生产者消费者模型的作用是什么?

(1) 通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率 ;

(2) 解耦,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约;

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!      

相关文章

  • springboot异常处理的基本规范

    springboot异常处理的基本规范

    这篇文章主要给大家介绍了关于springboot异常处理的基本规范,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Java经验点滴:处理没有被捕获的异常

    Java经验点滴:处理没有被捕获的异常

    Java经验点滴:处理没有被捕获的异常...
    2006-12-12
  • 详解Java中的四种引用类型(强软弱虚)

    详解Java中的四种引用类型(强软弱虚)

    Java中的引用类型主要分为四种,分别是强引用、软引用、弱引用和虚引用,这篇文章主要为大家详细介绍了四者的使用与区别,需要的小伙伴可以参考下
    2023-10-10
  • SpringBoot短链接跳转的代码实现

    SpringBoot短链接跳转的代码实现

    短链跳转是一种通过将长链接转换为短链接的方式,以便在互联网上进行链接共享和传播的技术,短链将原始长链接通过特定算法转换为较短的链接,使得它更容易分享、传播和展示,本文给大家介绍了SpringBoot短链接跳转的代码实现,需要的朋友可以参考下
    2024-03-03
  • SpringBoot集成Redisson实现延迟队列的场景分析

    SpringBoot集成Redisson实现延迟队列的场景分析

    这篇文章主要介绍了SpringBoot集成Redisson实现延迟队列,本文通过场景分析实例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Dapr在Java中的服务调用实战过程详解

    Dapr在Java中的服务调用实战过程详解

    这篇文章主要为大家介绍了Dapr在Java中的服务调用实战过程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • 深入理解Java8双冒号::的使用

    深入理解Java8双冒号::的使用

    这篇文章主要介绍了深入理解Java8双冒号::的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 详解SpringBoot开发案例之整合Dubbo分布式服务

    详解SpringBoot开发案例之整合Dubbo分布式服务

    这篇文章主要介绍了详解SpringBoot开发案例之整合Dubbo分布式服务,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • IntelliJ IDEA 编辑器的全局搜索中使用正则表达式的操作方法

    IntelliJ IDEA 编辑器的全局搜索中使用正则表达式的操作方法

    这篇文章主要介绍了IntelliJ IDEA 编辑器的全局搜索中使用正则表达式的相关知识,补充介绍了IDEA查找和替换实用正则,感兴趣的朋友跟随小编一起看看吧
    2024-01-01
  • SpringBoot 使用 Sa-Token 完成注解鉴权功能(权限校验)

    SpringBoot 使用 Sa-Token 完成注解鉴权功能(权限校验)

    Sa-Token 是一个轻量级 java 权限认证框架,主要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相关问题,这篇文章主要介绍了SpringBoot使用Sa-Token完成注解鉴权功能,需要的朋友可以参考下
    2023-05-05

最新评论