前言:

我们都听说过生产者和消费者的例子吧,现在来模拟一下。生产者生产面包,消费者消费面包。假定生产者将生成出来的面包放入篮子中,消费者从篮子中取。这样,当篮子中没有面包时,消费者不能取。当篮子满了以后,消费者不能一直生产。

思考:

使用面向对象的思想进行分析,涉及的事物有:生产者、消费者、篮子和面包。两个线程有:生产者的生产行为、消费者的消费行为。

Bread面包类

class Bread{int id;Bread(int id){this.id=id;}public String toString(){return "Bread:"+id;}
}

SyncStack篮子类

在篮子中,一般先放入的面包最后才取出,因此这里可以模拟数据结构栈的操作,提供了两个方法push()和pop()。

这里,假定篮子的最大容量为6个面包。

class SyncStack{int index=0;Bread[] arrWT=new Bread[6];//1、放入public synchronized void push(Bread wt){//装满了//if(index==arrWT.length){  防止发生异常之后,篮子满了仍然生成出现问题while(index==arrWT.length){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//叫醒wait中的线程this.notify();//this.notifyAll(); //叫醒所有arrWT[index]=wt;index++;}//2、拿出public synchronized Bread pop(){//篮子空了while(index==0){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}this.notify();index--;return arrWT[index];};
}

生产者和消费者

终于轮到线程类出场了!生产者Producer实现如下,消费者Consumer类也十分类似,只是不再具备生产面包的功能,并且将push方法换成了pop,在这里就不展示了。

class Producer implements Runnable{
//持有框的引用SyncStack ss=null;Producer(SyncStack ss){this.ss=ss;}@Overridepublic void run() {for (int i = 1; i < 20; i++) {Bread wt=new Bread(i);//扔到框里ss.push(wt);System.out.println("producer:"+wt);try {Thread.sleep((long) (Math.random()*1000));} catch (InterruptedException e) {e.printStackTrace();}}}}

测试:

这里我们简单的测试一下,一个生产者和一个消费者。

public static void main(String[] args) {SyncStack ss=new SyncStack();Producer p=new Producer(ss);Consumer c=new Consumer(ss);new Thread(p).start();new Thread(c).start();}

执行结果:

基本符合前面的要求

分析:

1、synchronized

一种同步锁,可以修饰代码块、方法等。当一个线程访问其修饰的内容时,视图访问该对象的线程将被阻塞。举个小例子:

/*** 获得当前时间*/public synchronized static void getTime() {System.out.println("1、进入获取时间方法=====");Date date = new Date();DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String time = format.format(date);System.out.println("2、"+Thread.currentThread().getName() + ":" + time);System.out.println("3、获取时间成功=====");System.out.println();}

我们启100次线程去调用他,在没有加synchronized关键字时,截取部分打印结果如下图:

加上关键字之后,打印结果如下:

可以很容易看出,在该方法前加上synchronized关键字之后,其他线程如果想继续访问,就会被阻塞,等待该线程执行结束后,其他线程才可以继续访问。

在上面消费者和生成者的例子中,我们在SyncStack的push和pop方法前都加上了synchronized关键字,就是为了保证“放”和“取”的原子性,这样模拟的结果才是合理的。

那么有人可能会问,生产者生产馒头和消费者消费馒头是否需要保持原子性,同一时间只允许一个线程执行呢?结合实际情况,可能有多个生产者、消费者同时进行操作,因此,这里不应做限制。

2、sleep()和wait()

在上面的例子中,Producer和Consumer内部使用的Thread.sleep()方法,而在SyncStack中的push和pop中却是用的wait()方法。这两个方法看似功能类似,有什么差别呢?

首先,翻到他们的根去看定义。注释看不懂可以先不看,不过注释就把他们的区别都说明白了。

wait()

public class Object {/*** Causes the current thread to wait until another thread invokes the* {@link java.lang.Object#notify()} method or the* {@link java.lang.Object#notifyAll()} method for this object.* In other words, this method behaves exactly as if it simply* performs the call {@code wait(0)}.* <p>* The current thread must own this object's monitor.*/public final void wait() throws InterruptedException {<span style="white-space:pre">   wait(0);</span>}
}

sleep()

public
class Thread implements Runnable {/*** Causes the currently executing thread to sleep (temporarily cease* execution) for the specified number of milliseconds, subject to* the precision and accuracy of system timers and schedulers. The thread* does not lose ownership of any monitors.*/public static native void sleep(long millis) throws InterruptedException;}

先说点肤浅的差别:

wait()是Object类中的方法,sleep()是Thread类中的。sleep必须指明挂起或睡眠的时间,而wait不需要。

上面注释中说的:

sleep()方法——占着CPU睡觉:CPU部分资源别占用,其他线程无法进入,但并不会失去他的监控,等到指定时间到了以后,继续恢复执行。

wait()  方法——等待使用CPU:线程会放弃对象锁,直到收到notify()通知才会进入准备状态。

我的理解:

在该实例中,push和pop中在“篮子空了”、“篮子装满了”的情况下使用了wait()方法。此时,已经无法继续工作下去,需要让出CPU给其他操作,才能保证生产-消费线进行。等到"篮子中有面包"情况时,调用notify通知线程执行。

Producer和Consumer中的sleep()方法是为了让放大彼此交替的差距,来接近现实生活的状态。

java线程(2)——模拟生产者与消费者相关推荐

  1. Java线程同步-模拟买票

    文章首发于 2020-11-29 知乎文章:Java线程同步-模拟买票 作者:落雨湿红尘(也是我o) 01 导语 本文使用JAVA代码模拟买票场景下的业务交互,通过示例讲解线程的初始化.线程同步等ja ...

  2. 多线程:线程同步与死锁(卖票案例)、线程通信、生产者与消费者

    卖票案例 5个窗口同时卖票: 使用Runnable接口,只创建了一个ticket1对象,5个线程共享此对象,实现了资源共享. public class ticket1 implements Runna ...

  3. Java基础进阶多线程-生产者和消费者模式

    1.什么是"生产者和消费者模式"? 生产线程负责生产,消费线程负责消费 生产线程和消费线程要达到均衡 这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方 ...

  4. java 多线程经典例子——生产者与消费者的问题

    产品名称类: public class Product {//产品名称private String name;public String getName() {return name;}public ...

  5. 详细讲解如何使用Java连接Kafka构建生产者和消费者(带测试样例)

    1 缘起 学习消息队列的过程中,先补习了RabbitMQ相关知识, 接着又重温了Kafka相关的知识, 发现,我并没有积累Java原生操作Kafka的文章, 只使用SpringBoot集成过Kafka ...

  6. 线程通信问题--生产者和消费者问题

    一.问题引入:首先实现一个线程通信的实例,使用两个线程交替打印输出100以内的数字. 代码实现如下: 1 package com.baozi.exer; 2 3 public class Commun ...

  7. 一个使用Java BlockingQueue实现的生产者和消费者

    消费者 package consumer;import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit ...

  8. Java多线程2.3.生产者与消费者之间的关系2

    生产者与消费者之间的关系 1.线程间通信问题描述图 2.线程的状态转换图及常见执行情况 3.等待唤醒机制思路图解 4.线程的生命周期

  9. 线程案例:生产者与消费者

    在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程. 在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据. 同样的道理,如 ...

最新文章

  1. MySQL · 捉虫动态 · event_scheduler 慢日志记错
  2. 马化腾很受伤 后果很严重
  3. Android中BaseAdapter使用总结(imooc笔记)
  4. 2020 MCM Meritorious Winner
  5. wether.html5.qq.com,人教版英语九年级全一册Unit 1单元测试卷及参考答案解析(含听力...
  6. Flask 学习 (二) blueprint 示例
  7. html rpc文档,HTML5 DRM 正式成为 Web 标准,百度正式开源RPC框架brpc
  8. Python+OpenCV:尺度不变特征变换 (SIFT, Scale-Invariant Feature Transform)
  9. Android Spinner填充数据后设置默认值的问题
  10. In the beginning, the world was void and without form…
  11. python学习多久可以就业_南昌多长时间能学会python(Python能去从事的工作)
  12. Centos中源码安装mysql
  13. 春节没事,看几部黑客电影吧!
  14. 楪祈机器人_饥荒联机版Inori楪祈MOD
  15. 潦草字体在线识别_潦草字体在线识别_遇到好看的字体?不会识别?教你如何快速识别字体...
  16. 三个月时间,如何成就自己成为一名数据分析师
  17. 关于Oracle数据库如何查询非当前用户名命名空间下的表不需要写ower直接from 表名
  18. 视频教程-Kali Linux渗透测试全程课与脚本语言编程系列课程-渗透测试
  19. 市场调研报告-全球与中国4K手术显示器市场现状及未来发展趋势
  20. CUDA out of memory解决办法

热门文章

  1. 一种快捷简便的WIN CE界面定制方法
  2. 时间序列预测之--ARIMA模型
  3. 冻成狗啦 我用Python爬取某宝2008条棉袄,终于买到心仪的棉袄了
  4. FPGA跨时钟信号同步简单处理方法
  5. Failed to load session “ubuntu”的解决办法
  6. 你对MANIFEST.MF这个文件知道多少?
  7. 生信分析平台使用指导
  8. Python Flask基本使用以及与Pytorch整合
  9. 鸿蒙系统4月份升级,华为旗舰手机将在4月起升级鸿蒙系统
  10. 全国职业院校技能大赛网络建设与运维赛项赛题(三)