前言:

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

思考:

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

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;
}
@Override
public 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 线程的通讯--生产者和消费者

    package 生产者和消费者; //消费者 public class Customer implements Runnable { private Share_resources rescource ...

  2. JAVA线程通信之生产者与消费者

    package cn.test.hf.test3; import java.util.concurrent.locks.Condition;import java.util.concurrent.lo ...

  3. java 线程并发(生产者、消费者模式)

    线程并发协作(生产者/消费者模式) 多线程环境下,我们经常需要多个线程的并发和协作.这个时候,就需要了解一个重要的多线程并发协作模型“生产者/消费者模式”. Ø 什么是生产者? 生产者指的是负责生产数 ...

  4. Java线程间通信--生产者消费者

    class ProducerConsumerDemo {    public static void main(String[] args)     {        Resource r = new ...

  5. 线程操作案例--生产者与消费者,Object类对线程的支持

    本章目标 1)加深对线程同步的理解 2)了解Object类中对线程的支持方法. 实例 生产者不断生产,消费者不断消费产品. 生产者生产信息后将其放到一个区域中,之后消费者从区域中取出数据. 既然生产的 ...

  6. java多线程中的生产者与消费者之等待唤醒机制@Version1.0

    一.生产者消费者模式的学生类成员变量生产与消费demo,第一版1.等待唤醒:    Object类中提供了三个方法:    wait():等待    notify():唤醒单个线程    notify ...

  7. Java并发编程(4)--生产者与消费者模式介绍

    一.前言 这种模式在生活是最常见的,那么它的场景是什么样的呢? 下面是我假象的,假设有一个仓库,仓库有一个生产者和一个消费者,消费者过来消费的时候会检测仓库中是否有库存,如果没有了则等待生产,如果有就 ...

  8. 多进程(了解):守护进程,互斥锁,信号量,进程Queue与线程queue(生产者与消费者模型)

    一.守护进程 主进程创建守护进程,守护进程的主要的特征为:①守护进程会在主进程代码执行结束时立即终止:②守护进程内无法继续再开子进程,否则会抛出异常. 实例: from multiprocessing ...

  9. Java多线程设计模式(2)生产者与消费者模式

    1 Producer-Consumer Pattern Producer-Consumer Pattern主要就是在生产者与消费者之间建立一个“桥梁参与者”,用来解决生产者线程与消费者线程之间速度的不 ...

随机推荐

  1. windows 开启 nginx 监听80 端口 以及 禁用 http 服务后,无法重启 HTTP 服务,提示 系统错误 123,文件目录、卷标出错

    1. 正常情况直接运行  start nginx.exe 不能开启成功,因为 80 端口被占用.提示: bind() to 0.0.0.0:80 failed (10013: An attempt w ...

  2. 剑指offer—二维数组中的查找

    题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...

  3. vue过渡动画效果

    1 过渡基础 1.1 过渡的方式 Vue 在插入.更新或者移除 DOM 时,提供多种不同方式的应用过渡效果. 包括以下工具: 在 CSS 过渡和动画中自动应用 class 可以配合使用第三方 CSS ...

  4. Hello,移动WEB—Viewport_Meta标签

    二   Viewport meta标签: 语法:<meta name="viewport" content="name=value, name=value" ...

  5. PHP实现微信红包算法和微信红包的架构设计简介

    微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...

  6. 【Hbase一】基础

    此笔记仅用于作者记录复习使用,如有错误地方欢迎留言指正,作者感激不尽,如有转载请指明出处 Hbase基础 Hbase基础 Hbase定义 行存储 v s 列存储 Hbase数据模型 Hbase物理模型 ...

  7. Windows使用Node.js自动生成Vue.js模版环境部署步骤-----记录

    node.js官网下载并安装node 进入node文档目录下,运行cmd 输入 node -v 查看node版本 出现表示安装完成 输入 npm -v 显示npm版本信息 安装cnpm 输入 npm ...

  8. 11个简单实用技巧--Java性能调优

    多数开发人员认为性能优化是个比较复杂的问题,需要大量的经验和知识.是的,这并不没有错.诚然,优化应用程序以获得最好的性能并不是一件容易的事情,但这并不意味着你在没有获得这些经验和知识之前就不能做任何事 ...

  9. QOS-QOS(服务质量)概述

    QOS-QOS(服务质量)概述 2018年7月7日  20:29 概述及背景: 1.  引入: 传统IP网络仅提供“尽力而为”的传输服务,网络有可用资源就转发,资源不足时就丢弃 新一代IP网络承载了 ...

  10. (数据科学学习手札31)基于Python的网络数据采集(初级篇)

    一.简介 在实际的业务中,我们手头的数据往往难以满足需求,这时我们就需要利用互联网上的资源来获取更多的补充数据,但是很多情况下,有价值的数据往往是没有提供源文件的直接下载渠道的(即所谓的API),这时 ...