通过多线程实现一个简单的生产者-消费者案例(笔记).

首先定义一个要生产消费的数据类 :

  1. public class Data {
  2.  
  3. private String id;
  4.  
  5. private String name;
  6.  
  7. public Data(String id, String name) {
  8. this.id = id;
  9. this.name = name;
  10. }
  11.  
  12. public String getId() {
  13. return id;
  14. }
  15.  
  16. public void setId(String id) {
  17. this.id = id;
  18. }
  19.  
  20. public String getName() {
  21. return name;
  22. }
  23.  
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27.  
  28. @Override
  29. public String toString() {
  30. return "Data{" +
  31. "id='" + id + '\'' +
  32. ", name='" + name + '\'' +
  33. '}';
  34. }
  35. }

生产者首先需要一个装载数据的容器, 产生的数据, 需要往里面放.

  1. public class Provider implements Runnable {
  2.  
  3. //1. 装载数据的容器
  4. private BlockingQueue<Data> queue;
  5.  
  6. //2. 运行标志
  7. private volatile boolean isRunning = true;
  8.  
  9. //3. 为数据产生id
  10. private static AtomicInteger count = new AtomicInteger();
  11.  
  12. //4. 随机休眠
  13. private static Random r = new Random();
  14.  
  15. public Provider(BlockingQueue queue){
  16. this.queue = queue;
  17. }
  18.  
  19. @Override
  20. public void run() {
  21. while (isRunning){
  22. try {
  23. //随机休眠, 模拟产生数据逻辑耗时
  24. Thread.sleep(r.nextInt(100));
  25. //产生一个数据id, 为了避免多线程产生的id重复, 所以这里使用 AtomicInteger
  26. int id = count.incrementAndGet();
  27. //创建数据
  28. Data data = new Data(Integer.toString(id), "data" + id);
  29. //打印创建日志
  30. System.out.println( Thread.currentThread().getName() + " ++++++ " + id);
  31. //将数据加载到容器中
  32. if(!this.queue.offer(data, 1, TimeUnit.SECONDS)){
  33. System.out.println(id + " 提交失败......");
  34. }
  35. }
  36. catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  41.  
  42. public void stop(){
  43. this.isRunning = false;
  44. }
  45.  
  46. public void reset(){
  47. this.isRunning = true;
  48. }
  49. }

消费者, 也需要知道数据存放的容器, 才能从里面拿取数据进行处理.

  1. public class Consumer implements Runnable {
  2.  
  3. //1. 数据容器, 从中取数据进行消费
  4. private BlockingQueue<Data> queue;
  5.  
  6. public Consumer(BlockingQueue<Data> queue){
  7. this.queue = queue;
  8. }
  9.  
  10. //随机休眠
  11. private static Random r = new Random();
  12.  
  13. @Override
  14. public void run() {
  15. while (true){
  16. try {
  17. //从队列中取数据
  18. Data data = this.queue.take();
  19. //如果获取到的数据为空, 则不进行处理
  20. if(data == null){
  21. continue;
  22. }
  23. //随机休眠, 模拟消费数据逻辑处理耗时
  24. Thread.sleep(r.nextInt(1000));
  25. //打印消费日志
  26. System.out.println( Thread.currentThread().getName() + " ------ " + data.getId());
  27. }
  28. catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }
  33. }

测试方法:

  1. public static void main(String[] args){
  2. //BlockingQueue<Data> queue = new LinkedBlockingQueue<>(16);
  3. BlockingQueue<Data> queue = new ArrayBlockingQueue<>(16);
  4. List<Provider> providers = new ArrayList<>();
  5. for (int i = 0; i < 10; i++) {
  6. Provider p = new Provider(queue);
  7. providers.add(p);
  8. }
  9.  
  10. List<Consumer> consumers = new ArrayList<>();
  11. for (int i = 0; i < 2; i++) {
  12. Consumer c = new Consumer(queue);
  13. consumers.add(c);
  14. }
  15.  
  16. ExecutorService pool = Executors.newCachedThreadPool();
  17. for (Provider provider : providers) {
  18. pool.execute(provider);
  19. }
  20.  
  21. for (Consumer consumer : consumers) {
  22. pool.execute(consumer);
  23. }
  24.  
  25. try {
  26. Thread.sleep(3000);
  27. }
  28. catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31.  
  32. for (Provider provider : providers) {
  33. provider.stop();
  34. }
  35. }

这里特意用了一个有界队列, 并且特意设置了生产者多于消费者.

打印日志:

+ 号代表产生了数据, 但是不一定加入到容器里了.

- 号代表消费成功, 数据从队列中删除

这里需要注意一个问题:

生产消费都是有速度的, 也就是说, 如果消费速度小于生产速度

1. 使用有界队列 - 会丢数据, 需要对这部分数据做特殊处理

2. 使用无界队列 - 可能会让电脑宕机, 数据积累越来越多, 导致内存不足, 卡死或者直接死机.

需要对应用场景进行分析, 才能决定使用哪种方式.

多线程笔记 - provider-consumer的更多相关文章

  1. 2016/1/25 多线程 作业 方法一 继承Thread 方法二 实现Runnable 多线程笔记

    /* * 1,尝试定义一个继承Thread类的类,并覆盖run()方法, * 在run()方法中每隔100毫秒打印一句话.*/ package Stream; //方法一 继承Thread 实现多线程 ...

  2. Java基础知识强化之多线程笔记01:多线程基础知识(详见Android(java)笔记61~76)

    1. 基础知识: Android(java)学习笔记61:多线程程序的引入    ~    Android(java)学习笔记76:多线程-定时器概述和使用 

  3. dubbo 学习笔记 -- provider端

    服务端的配置文件:    provider.xml <?xml version="1.0" encoding="UTF-8"?> <beans ...

  4. 如何理解springcloud微服务项目中,eureka,provider,consumer它们之间的关系?

    eureka负责注册provider和consumer的服务信息 provider负责与数据库进行交互,实现数据持久化,并给consumer提供服务 consumer与前端交互,通过与Eureka同源 ...

  5. 这份java多线程笔记,你真得好好看看,我还没见过总结的这么全面的

    1.线程,进程和多线程 1.程序:指指令和数据的有序集合,其本身没有任何意义,是一个静态的概念 2.进程:指执行程序的一次执行过程,是一个动态的概念.是系统资源分配的单位(注意:很多多线程是模拟出来的 ...

  6. iOS多线程笔记

    在iOS开发中,有三种多线程处理方式: 1. 利用NSThread 2. NSOperation和NSOperationQueue 3. 利用GCD(Grand Central Dispatch) 使 ...

  7. Java基础知识强化之多线程笔记05:Java中继承thread类 与 实现Runnable接口的区别

    1. Java中线程的创建有两种方式:  (1)通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中. (2)通过实现Runnable接口,实例化Thread类. 2. ...

  8. QT多线程笔记

    1.QT多线程涉及到主线程和子线程之间交互大量数据的时候,使用QThread并不方便,因为run()函数本身不能接受任何参数,因此只能通过信号和槽的交互来获取数据,如果只是单方面简单交互数据还过得去, ...

  9. Java基础知识强化之多线程笔记05:Java程序运行原理 和 JVM的启动是多线程的吗

    1. Java程序运行原理:     Java 命令会启动Java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程.该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 m ...

随机推荐

  1. 关于Hive中case when不准使用子查询的解决方法

    在公司用Hive实现个规则的时候,遇到了要查询某个字段是否在另一张表中,大概情况就是 A表: id value1 value2 1 100 0 2 101 1 3 102 1 B表: value1 1 ...

  2. ERR : undefined reference to something

    序言: define : 定义.相信你用过 #define PI 3.141592653 (千万记得别在这句代码后加分号) reference : 引用 undefined reference to ...

  3. MySql配置环境变量

    完成后安装好MySQL,为MySQL配置环境变量. 0)在我的电脑上点击右键选择属性-->高级系统设置-->环境变量1)新建MYSQL_HOME变量,并配置:D:\Develop\mysq ...

  4. 工具之cut

    转自http://www.cnblogs.com/dong008259/archive/2011/12/09/2282679.html cut:正如其名就是“剪”,和sed一样,一行为单位,对数据进行 ...

  5. php--->自己封装的简易版mvc框架

    最近根据自己的理解,封装了一个自己的框架,来重新系统化梳理自己对mvc框架的理解:后续会陆续添加各种新的功能. 欢迎指点交流. GitHub:https://github.com/Frankltf/m ...

  6. laravel 工厂模式到容器

    下面实现了查人拥有超能力的三种方式 第一种最基本的类引用实现 1 <?php /** * 目的:代码的完善来说明从 基础类的调用到 工厂类的使用 再到容器的出现的原因 * (首先要明白工厂类和容 ...

  7. springIOC源码接口分析(六):ResourceLoader

    参考博客: https://www.cnblogs.com/jixp/articles/10702486.html 一 定义方法 Spring提供了ResourceLoader接口用于实现不同的Res ...

  8. 【论文笔记系列】AutoML:A Survey of State-of-the-art (上)

    之前已经发过一篇文章来介绍我写的AutoML综述,最近把文章内容做了更新,所以这篇稍微细致地介绍一下.由于篇幅有限,下面介绍的方法中涉及到的细节感兴趣的可以移步到论文中查看. 论文地址:https:/ ...

  9. 《Android Studio实战 快速、高效地构建Android应用》--五、备忘录实验(1/2)

    通过开发App熟悉Android Studio的用法 开发一款用于管理备忘事项列表的App,核心功能: 创建.删除备忘 将某些备忘标记为重要(左侧带颜色标签突出显示) 涉及:操作栏菜单.上下文菜单.用 ...

  10. JavaScript 初学者容易犯的几个错误,你中招没?

    JavaScript 是对初学者比较友好的一门编程语言,基本上花个半小时看下语法就能写出能运行的代码.JavaScript 是动态脚本语言,对数据类型没有太多的限制,写起来非常灵活.但正因为如此,初学 ...