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

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

public class Data {

    private String id;

    private String name;

    public Data(String id, String name) {
this.id = id;
this.name = name;
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Data{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}

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

public class Provider implements Runnable {

    //1. 装载数据的容器
private BlockingQueue<Data> queue; //2. 运行标志
private volatile boolean isRunning = true; //3. 为数据产生id
private static AtomicInteger count = new AtomicInteger(); //4. 随机休眠
private static Random r = new Random(); public Provider(BlockingQueue queue){
this.queue = queue;
} @Override
public void run() {
while (isRunning){
try {
//随机休眠, 模拟产生数据逻辑耗时
Thread.sleep(r.nextInt(100));
//产生一个数据id, 为了避免多线程产生的id重复, 所以这里使用 AtomicInteger
int id = count.incrementAndGet();
//创建数据
Data data = new Data(Integer.toString(id), "data" + id);
//打印创建日志
System.out.println( Thread.currentThread().getName() + " ++++++ " + id);
//将数据加载到容器中
if(!this.queue.offer(data, 1, TimeUnit.SECONDS)){
System.out.println(id + " 提交失败......");
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
} public void stop(){
this.isRunning = false;
} public void reset(){
this.isRunning = true;
}
}

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

public class Consumer implements Runnable {

    //1. 数据容器, 从中取数据进行消费
private BlockingQueue<Data> queue; public Consumer(BlockingQueue<Data> queue){
this.queue = queue;
} //随机休眠
private static Random r = new Random(); @Override
public void run() {
while (true){
try {
//从队列中取数据
Data data = this.queue.take();
//如果获取到的数据为空, 则不进行处理
if(data == null){
continue;
}
//随机休眠, 模拟消费数据逻辑处理耗时
Thread.sleep(r.nextInt(1000));
//打印消费日志
System.out.println( Thread.currentThread().getName() + " ------ " + data.getId());
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

测试方法:

public static void main(String[] args){
//BlockingQueue<Data> queue = new LinkedBlockingQueue<>(16);
BlockingQueue<Data> queue = new ArrayBlockingQueue<>(16);
List<Provider> providers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Provider p = new Provider(queue);
providers.add(p);
} List<Consumer> consumers = new ArrayList<>();
for (int i = 0; i < 2; i++) {
Consumer c = new Consumer(queue);
consumers.add(c);
} ExecutorService pool = Executors.newCachedThreadPool();
for (Provider provider : providers) {
pool.execute(provider);
} for (Consumer consumer : consumers) {
pool.execute(consumer);
} try {
Thread.sleep(3000);
}
catch (InterruptedException e) {
e.printStackTrace();
} for (Provider provider : providers) {
provider.stop();
}
}

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

打印日志:

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

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

这里需要注意一个问题:

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

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

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

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

最新文章

  1. sscanf_强大的数据读取-转换
  2. sql select 综合运用
  3. HTML5 input事件检测输入框变化
  4. Java面试题大全(四)
  5. 正确理解SQL Server的许可证(转)
  6. MySQL 子查询 EXISTS 和 NOT EXISTS(转)
  7. Windows7如何在安全模式下卸载驱动(亲测)
  8. 代码bug
  9. Decimal Basic 学习笔记(1)
  10. 人人必知的10个jQuery小技巧
  11. MYSQL 简单的循环存储过程
  12. 3-04. 一元多项式的乘法与加法运算(20)(ZJU_PAT 结构体)
  13. python 数据清洗之字符串处理
  14. Uva140 Bandwidth 全排列+生成测试法+剪枝
  15. OpenStack(企业私有云)万里长征第六步——OpenStack网络及虚拟机存储位置
  16. 酷伯伯实时免费HTTP代理ip爬取(端口图片显示+document.write)
  17. c# linq lambda 去重,排序,取最高纪录。
  18. NetCore部署到Linux服务器+Supervisor的步骤及过程中踩过的坑
  19. MT【15】证明无理数(1)
  20. JS 中函数名后面加与不加括号的区别

热门文章

  1. 解决浮点运算精度不准确,BigDecimal 加减乘除
  2. 使用 CompletableFuture 异步组装数据
  3. springboot中的那些连接池
  4. 19_07_8校内训练[sort]
  5. Day10-Python3基础-协程、异步IO、redis缓存、rabbitMQ队列
  6. linux下误清用户/home下的文件怎么办?
  7. vscode打开文件,中文显示乱码(已解决)
  8. Python 类中方法的内部变量,命名加&#39;self.&#39;变成 self.xxx 和不加直接 xxx 的区别
  9. nmap详解之原理与用法
  10. 模块化系列教程 | 深入源码分析阿里JarsLink1.0模块化框架