线程的生产者和消费者模式

2023-05-16

多个线程同时运行时,会产生线程并发可使用同步操作确保数据的安全性,如果需要各线程之间交互,可是使用线程等待和唤醒模式,在这里常用的等待唤醒中经典的模式为“生产者和消费者模式”

生产者和消费者由两类线程组成: 若干个生产者线程 负责提交用户的请求,若干个消费者线程负责处理生成出来的任务。 他们操作一块共享内存区进生成/消费的产品(数据): Mobile (手机编号)

生成者线程类: Provider : 无限制的生成手机

消费者线程类:Customer : 无限制的消费手机

​ 共享存储区: Storage ( push 、pop) 存储手机的对象数组

​ 测试类 行数据通信。```java
public class Mobile {
private int id;

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public Mobile(int id) {
    this.id = id;
}

}


存储区

```java
package com.j2008.provider_customer;

/**
 * ClassName: Storage
 * Description:
 * date: 2020/11/10 15:32
 *  存储区,它是生产者和消费者共享的空间 ( 生产者和消费者将该对象作为公有锁)
 * @author wuyafeng
 * @version 1.0   softeem.com
 */
public class Storage {

    // 定义存储手机的对象数据
    Mobile [] mobiles = new Mobile[10];
    int index=0;  // 个数

    static int n=1000;
    /**
     * 存放手机
     * @param mobile
     */
    public synchronized void push(Mobile mobile){
        //考虑容器上限已满,必须等待
        while(index == mobiles.length){
            System.out.println("容器已满,需等待");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //通知消费者取消费 ,将其他线程全部唤醒
        this.notifyAll();
        mobiles[index]=mobile;
        index++;

    }

    /**
     * 取出手机      1 2 3 4
     *              1 2 3
     *              index--;
     *             mobile[index] =null
     * @return 取出的手机对象
     */
    public synchronized Mobile pop(){
        Mobile m = null;

        // 判断index是否小于0
        while(index<=0){
            //等待
            System.out.println("容器中没有手机,需要等待");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        index--;
        m = mobiles[index];
        //将容器中的这个位置 设置为空
        mobiles[index]=null;
        // 通知生产者去生产
        this.notifyAll();
        return m;

    }

    public synchronized int getSize(){
        return index;
    }

}

生产者:

package com.j2008.provider_customer;

/**
 * ClassName: Provider
 * Description:
 * date: 2020/11/10 15:54
 *
 * @author wuyafeng
 * @version 1.0   softeem.com
 */
public class Provider implements  Runnable {
    //共享存储区
    Storage storage =null;
    public Provider(Storage storage){
        this.storage = storage;
    }


    @Override
    public void run() {
        //手机编号
        int n=1000;
        //一直生产
        while(true){
            Mobile m = new Mobile(n);
            storage.push(m);
            System.out.println(Thread.currentThread().getName()+
                    "生产了一部手机,其编号:"+m.getId()+" 其库存:"+storage.getSize());

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            n++;
        }
    }
}

消费者

package com.j2008.provider_customer;

/**
 * ClassName: Customer
 * Description:
 * date: 2020/11/10 15:58
 *
 * @author wuyafeng
 * @version 1.0   softeem.com
 */
public class Customer implements  Runnable {

    Storage storage=null;
    public Customer(Storage storage){
        this.storage = storage;
    }

    @Override
    public void run() {
        while(true){
            Mobile mobile = storage.pop();
            System.out.println(Thread.currentThread().getName()+
                    "消费了一部手机,编号》》"+mobile.getId()+" 库存:"+storage.getSize());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试类

package com.j2008.provider_customer;

/**
 * ClassName: TestProviderCustomer
 * Description:
 * date: 2020/11/10 16:01
 *
 * @author wuyafeng
 * @version 1.0   softeem.com
 */
public class TestProviderCustomer {
    public static void main(String[] args) {
         //创建公有的存储空间
        Storage storage = new Storage();
        Provider provider1 = new Provider(storage);
        Provider provider2 = new Provider(storage);
        Provider provider3 = new Provider(storage);

        Thread th1 = new Thread(provider1,"张飞");
        Thread th2 = new Thread(provider2,"刘备");
        Thread th3 = new Thread(provider3,"关羽");

        th1.start();
        th2.start();
        th3.start();

        //消费者上场
        Customer customer1 = new Customer(storage);
        Customer customer2 = new Customer(storage);
        Customer customer3 = new Customer(storage);

        Thread th4 = new Thread(customer1,"张飞的老婆");
        Thread th5 = new Thread(customer2,"刘备的老婆");
        Thread th6 = new Thread(customer3,"关羽的老婆");

        th4.start();
        th5.start();
        th6.start();


    }
}

测试结果

关羽生产了一部手机,其编号:1000 其库存:1
张飞的老婆消费了一部手机,编号》》1000 库存:0
张飞生产了一部手机,其编号:1000 其库存:1
刘备生产了一部手机,其编号:1000 其库存:2
刘备的老婆消费了一部手机,编号》》1000 库存:1
关羽的老婆消费了一部手机,编号》》1000 库存:0
容器中没有手机,需要等待
关羽生产了一部手机,其编号:1001 其库存:0
张飞的老婆消费了一部手机,编号》》1001 库存:0
张飞生产了一部手机,其编号:1001 其库存:2
刘备生产了一部手机,其编号:1001 其库存:2
关羽的老婆消费了一部手机,编号》》1001 库存:0
刘备的老婆消费了一部手机,编号》》1001 库存:0
容器中没有手机,需要等待
关羽生产了一部手机,其编号:1002 其库存:0
张飞的老婆消费了一部手机,编号》》1002 库存:0
刘备生产了一部手机,其编号:1002 其库存:2
张飞生产了一部手机,其编号:1002 其库存:2
刘备的老婆消费了一部手机,编号》》1002 库存:1
关羽的老婆消费了一部手机,编号》》1002 库存:0
```## 1、定义

​        用于创建和管理线程的容器就是线程池  (Thread Pool) ,在线程池中的线程执行完任务后不会立马进入销毁状态,而是重置到线程池中变为“空闲线程” 。  有利于避免频繁创建线程消耗资源,提供线程复用率,有限管理该线程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lhcrieyn-1605486918300)(assets/1604998934958.png)]

   2、使用线程池的原因:

​       在多线程环境下,对于不断创建和销毁效率非常消耗系统资源,对于多线程之间的切换存在线程安全问题, 这是使用统一的管理类管理一些线程是比较好的解决办法

3、线程的运行机制:

- ​       在线程池模式下,任务是提交给线程池,由线程池根据当前空闲线程进行分配任务,如果没有空闲线程,由管理类创建线程或者进入任务等待队列中。
- ​       一个线程同时只能执行一个任务,但多个任务可以同时提交给这个线程池。

​    

线程池的常用类 (ExecutedService)        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
                                       参数1:  corePoolSize:核心线程数

参数2:maximumPoolSize :最大线程数

参数3: keepAliveTime : 线程活动时长  

参数4: 对于参数3的单位 

  1、可缓存的线程池   newCacheThreadPool(n);如果线程池中没有空闲线程,则创建新线程并放入线程池中,无上限线程数,如果有空闲线程则直接使用该线程
  public static void main(String[] args) {
        // 创建可缓存线程
        ExecutorService service =Executors.newCachedThreadPool();
        // 创建10个线程
        int n=0;
        for(int i = 0 ;i < 10;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            service.execute(new Runnable() {
                @Override  //你们内部类
                public void run() {
                      //任务
                    System.out.println(Thread.currentThread().getName()+"---"+n);
                    try {

                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            });
            //由于以上的线程 每次执行完成只需要500毫秒 ,会释放线程对象到线程池中
            //  1000毫秒后创建的线程 就会复用上一次的线程对象


        }

        //关闭线程池
        service.shutdown();
    }
}
2、可重用的固定线程池: newFixedThreadPool(n)  ,线程数量固定,如果没有空闲线程,则存放无界队列中等待 
   public static void main(String[] args) {
         //创建线程池
        ExecutorService service = Executors.newFixedThreadPool(3);
        // 连续创建10个线程, 由于只有3个线程,所有线程只能等待
        // 2秒后再执行3个
        for(int i =0;i<10;i++){
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"正在执行");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        // 结果: 每2秒执行3个线程 
    }
    4、单线程的线程池 newSingleThreadExecutor : 线程池中只有一个线程数,所有的任务都通过该线程执行,它可以保证所有的任务是FIFO模式, 满足队列结构 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程的生产者和消费者模式 的相关文章

  • Linux ping不通,连不上网的解决办法

    Linux ping不通 xff0c 连不上网的解决办法 可能原因是DNS没有配置好 方法一 xff1a 修改vi etc resolv conf 增加如下内容 xff1a nameserver 114 114 114 114 电信的DNS
  • Android——多进程

    之前我们了解了 Java 多线程浅析 Android Handler详解 Android HandlerThread浅析 Java ThreadPool线程池 让我们继续看看Android多进程 xff1a 1 概述 默认情况下 xff0c
  • React、Ant Desgin自定义加载动画,lottie-web 将json解析成动画

    在项目中 xff0c 遇到需要在网页首屏 xff0c 展示动画的需求 xff0c 你会想到怎么做 xff1f 思路一 xff1a 设计师导出gif图片 xff0c 用img进行展示 缺点 xff1a 图片失真 xff0c 影响效果 思路二
  • mac下安装多版本PHP及切换

    mac下安装多版本PHP及切换 工作环境一直是PHP5 6 xff0c 后来发布了PHP7 xff0c 性能提升不少 xff0c 如今打算试试PHP7 xff0c 所以就有了两个php版本的需求 本文的原理就是用一个php管理工具 xff0
  • 短视频拍摄脚本怎么写

    优质的短视频每一个镜头都经过精心设计 xff0c 镜头的设计就是利用镜头脚本 xff0c 提前设想好一切想要的镜头效果和画面 xff0c 最终作品才能一气呵成的呈现出来 xff0c 接下来就来分析一下短视频拍摄脚本怎么写 xff0c 短视频
  • 串口开发之环形缓冲区

    01 简介 串口的基本应用 xff0c 使用串口中断接收数据 xff0c 串口中断发送回包 xff08 一般可以使用非中断形式发送回包 xff0c 在数据接收不频繁的应用中 串口接收中断保证串口数据及时响应 xff0c 使用非中断方式发送回
  • fastboot 命令

    1 fastboot概念 fastboot fastboot是PC与bootloader的USB通信的命令行工具 xff0c 通过向bootloader传送刷机文件 xff08 img xff09 实现Android系统分区重烧 fastb
  • Android Studio 开启视图绑定 viewBinding

    Google 在 Android Studio 3 6 Canary 11 及更高版本中提供了一个 viewBinding 的开关 xff0c 可以开启视图绑定功能 xff0c 以此来替代 findViewById viewBinding功
  • ViewPager 装载fragment 页面显示空白

    ViewPager 装载fragment 页面显示空白 xff0c 这个时候有两种情况 xff1a 在分页面较多的情况下 使用了 FragmentPagerAdapter xff0c 可能会导致第二次加载页面显示空白或是多次滑动页面后页面空
  • The following packages have unmet dependencies: openssh-server : Depends: openssh-client (= 1:6.6p1

    在虚拟机中安装openssh server的时候报了这个错误 xff0c 不知道这台虚拟机抽了什么风 xff0c 别的虚拟机都能顺利安装 xff0c xff0c xff0c 提示说是openssh server 依赖于 openssh cl
  • Docker Desktop stopped 问题解决

    推广博客 xff1a Docker Desktop stopped 问题解决
  • windows连接远程桌面必须要有用户名和密码

    被远程连接的电脑如果有用户名但没有密码 xff0c 连接时需要输入密码时空着会导致无法连接 想想也是 xff0c 如果没有密码 xff0c 只要有人连入电脑所在局域网 xff0c 就可以通过ip地址和用户名连入电脑 xff0c 非常不安全
  • Android中APK签名工具之jarsigner和apksigner详解

    一 工具介绍 jarsigner是JDK提供的针对jar包签名的通用工具 位于JDK bin jarsigner exe apksigner是Google官方提供的针对Android apk签名及验证的专用工具 位于Android SDK
  • Android NumberPicker的基本用法及常见问题汇总

    前言 在项目中需要一个选择人数的控件 xff0c 于是想到了NumberPicker xff0c 这个控件相对不是那么热门 xff0c 我也是第一次用 xff0c 所以遇到了一些问题 xff0c 这里做个小结 正文 首先来看一下最终的效果
  • angular将html代码输出为内容

    在前端与后台的撕逼中 xff0c 很大一部分是因为数据的问题 使用angular会遇到这样的问题 xff0c 后台返回的数据不是自己想要的纯字符串 xff0c 而是带有html标签及属性的 xff0c 那么我们将它输出来后 xff0c 在页
  • Jetpack新成员,App Startup一篇就懂

    Android 11系统已经来了 xff0c 随之而来的是 xff0c Jetpack家族也引入了许多新的成员 其实以后Android的更新都会逐渐采用这种模式 xff0c 即特定系统相关的API会越来越少 xff0c 更多的编程API是以
  • appWidget

    构建应用微件 应用微件是可以嵌入其他应用 xff08 如主屏幕 xff09 并接收定期更新的微型应用视图 这些视图称为界面中的微件 xff0c 您可以使用应用微件提供程序发布微件 能够容纳其他应用微件的应用组件称为应用微件托管应用 下面的屏
  • Jetpack新成员,Paging3从吐槽到真香

    各位小伙伴们大家早上好 随着Android 11的正式发布 xff0c Jetpack家族也引入了许多新的成员 我之前有承诺过 xff0c 对于新引入的App Startup Hilt Paging 3 xff0c 我会分别写一篇文章进行介
  • kotlin--综合运用Hilt、Paging3、Flow、Room、Retrofit、Coil等实现MVVM架构

    前面我们使用Java来运用JetPack中的一系列组件 xff0c 又使用kotlin运用这些组件实现了一系列功能 xff1a kotlin Flow文件下载kotlin Flow结合Room运用kotlin Flow结合retrofit运
  • kotlin基本类型

    基本类型 在 Kotlin 中 xff0c 所有东西都是对象 xff0c 在这个意义上讲我们可以在任何变量上调用成员函数与属性 一些类型可以有特殊的内部表示 例如 xff0c 数字 字符以及布尔可以在运行时表示为原生类型值 xff0c 但是

随机推荐