线程并发协作(生产者/消费者模式)

多线程环境下,我们经常需要多个线程的并发和协作。这个时候,就需要了解一个重要的多线程并发协作模型“生产者/消费者模式”。

Ø 什么是生产者?

生产者指的是负责生产数据的模块(这里模块可能是:方法、对象、线程、进程)。

Ø 什么是消费者?

消费者指的是负责处理数据的模块(这里模块可能是:方法、对象、线程、进程)。

Ø 什么是缓冲区?

消费者不能直接使用生产者的数据,它们之间有个“缓冲区”。生产者将生产好的数据放入“缓冲区”,消费者从“缓冲区”拿要处理的数据。

缓冲区是实现并发的核心,缓冲区的设置有3个好处:

Ø 实现线程的并发协作

有了缓冲区以后,生产者线程只需要往缓冲区里面放置数据,而不需要管消费者消费的情况;同样,消费者只需要从缓冲区拿数据处理即可,也不需要管生产者生产的情况。 这样,就从逻辑上实现了“生产者线程”和“消费者线程”的分离。

Ø 解耦了生产者和消费者

生产者不需要和消费者直接打交道。

Ø 解决忙闲不均,提高效率

生产者生产数据慢时,缓冲区仍有数据,不影响消费者消费;消费者处理数据慢时,生产者仍然可以继续往缓冲区里面放置数据 。

生产者与消费者模式

public class TestProduce {
public static void main(String[] args) {
SyncStack sStack = new SyncStack();// 定义缓冲区对象;
Shengchan sc = new Shengchan(sStack);// 定义生产线程;
Xiaofei xf = new Xiaofei(sStack);// 定义消费线程;
sc.start();
xf.start();
}
} class Mantou {// 馒头
int id; Mantou(int id) {
this.id = id;
}
} class SyncStack {// 缓冲区(相当于:馒头筐)
int index = 0;
Mantou[] ms = new Mantou[10]; public synchronized void push(Mantou m) {
while (index == ms.length) {//说明馒头筐满了
try {
//wait后,线程会将持有的锁释放,进入阻塞状态;
//这样其它需要锁的线程就可以获得锁;
this.wait();
//这里的含义是执行此方法的线程暂停,进入阻塞状态,
//等消费者消费了馒头后再生产。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 唤醒在当前对象等待池中等待的第一个线程。
//notifyAll叫醒所有在当前对象等待池中等待的所有线程。
this.notify();
// 如果不唤醒的话。以后这两个线程都会进入等待线程,没有人唤醒。
ms[index] = m;
index++;
} public synchronized Mantou pop() {
while (index == 0) {//如果馒头筐是空的;
try {
//如果馒头筐是空的,就暂停此消费线程(因为没什么可消费的嘛)。
this.wait(); //等生产线程生产完再来消费;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
index--;
return ms[index];
}
} class Shengchan extends Thread {// 生产者线程
SyncStack ss = null; public Shengchan(SyncStack ss) {
this.ss = ss;
} @Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("生产馒头:" + i);
Mantou m = new Mantou(i);
ss.push(m);
}
}
} class Xiaofei extends Thread {// 消费者线程;
SyncStack ss = null; public Xiaofei(SyncStack ss) {
this.ss = ss;
} @Override
public void run() {
for (int i = 0; i < 10; i++) {
Mantou m = ss.pop();
System.out.println("消费馒头:" + i); }
}
}

执行结果如图:

线程并发协作总结:

线程并发协作(也叫线程通信),通常用于生产者/消费者模式,情景如下:

1. 生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。

2. 对于生产者,没有生产产品之前,消费者要进入等待状态。而生产了产品之后,又需要马上通知消费者消费。

3. 对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产新产品以供消费。

4. 在生产者消费者问题中,仅有synchronized是不够的。

· synchronized可阻止并发更新同一个共享资源,实现了同步;

· synchronized不能用来实现不同线程之间的消息传递(通信)。

5. 那线程是通过哪些方法来进行消息传递(通信)的呢?见如下总结:

6. 以上方法均是java.lang.Object类的方法;

都只能在同步方法或者同步代码块中使用,否则会抛出异常。

 

在实际开发中,尤其是“架构设计”中,会大量使用这个模式。 对于初学者了解即可,如果晋升到中高级开发人员,这就是必须掌握的内容。

java 线程并发(生产者、消费者模式)的更多相关文章

  1. Java多线程-同步:synchronized 和线程通信:生产者消费者模式

    大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...

  2. Java设计模式之生产者消费者模式

    Java设计模式之生产者消费者模式 博客分类: 设计模式 设计模式Java多线程编程thread 转载 对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一 ...

  3. java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-【费元星Q9715234】

    java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-[费元星Q9715234] 说明如下,不懂的问题直接我[费元星Q9715234] 1.反射的意义在于不将xml tag ...

  4. 第23章 java线程通信——生产者/消费者模型案例

    第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...

  5. .net学习之多线程、线程死锁、线程通信 生产者消费者模式、委托的简单使用、GDI(图形设计接口)常用的方法

    1.多线程简单使用(1)进程是不执行代码的,执行代码的是线程,一个进程默认有一个线程(2)线程默认情况下都是前台线程,要所有的前台线程退出以后程序才会退出,进程里默认的线程我们叫做主线程或者叫做UI线 ...

  6. java设计模式之生产者/消费者模式

    什么是生产者/消费者模式? 某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产者:而处理数据的模块,就称为消费者 ...

  7. 【多线程】java多线程实现生产者消费者模式

    思考问题: 1.为什么用wait()+notify()实现生产者消费者模式? wait()方法可以暂停线程,并释放对象锁 notify()方法可以唤醒需要该对象锁的其他线程,并在执行完后续步骤,到了s ...

  8. Java实现多线程生产者消费者模式的两种方法

    生产者消费者模式:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据.生产者生产一个,消费者消费一个,不断循环. 第一种实现方法,用BlockingQueue阻塞队 ...

  9. Java多线程-----实现生产者消费者模式的几种方式

       1 生产者消费者模式概述 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理 ...

  10. Java多线程_生产者消费者模式2

    在我的上一条博客中,已经介绍到了多线程的经典案列——生产者消费者模式,但是在上篇中用的是传统的麻烦的非阻塞队列实现的.在这篇博客中我将介绍另一种方式就是:用阻塞队列完成生产者消费者模式,可以使用多种阻 ...

随机推荐

  1. 【Java/Json】Java对Json进行建模,分词,递归向下解析构建Json对象树

    伸手党的福音 代码下载:https://files.cnblogs.com/files/xiandedanteng/JsonLexerBuilder20191202.rar 互联网上成型的对Json进 ...

  2. GridLayout: GridLayout使用简介(转)

    Android 布局之GridLayout 1 GridLayout简介 GridLayout是Android4.0新提供的网格矩阵形式的布局控件. GridLayout的继承关系如下:java.la ...

  3. nxp基于layerscape系列芯片的硬件型号解析

    每一种layerscape系列芯片都有两种硬件型号: RDB 和QDS RDB: Refrence Design Board QDS: QorIQ Development system

  4. Java基础 do-while 简单示例

        JDK :OpenJDK-11      OS :CentOS 7.6.1810      IDE :Eclipse 2019‑03 typesetting :Markdown   code ...

  5. flutter Switch组件 On/off 用于切换一个单一状态

    import 'package:flutter/material.dart'; class SwitchDemo extends StatefulWidget { @override _SwitchD ...

  6. Dart 变量、常量和命名规则

    /* Dart 变量: dart是一个强大的脚本类语言,可以不预先定义变量类型 ,自动会类型推导 dart中定义变量可以通过var关键字可以通过类型来申明变量 如: var str='this is ...

  7. 算法习题---4-8特别困的学生(UVa12108)

    一:题目 课堂上有n个学生(n<=),每个学生上课都会出现一个“清醒-睡眠”周期,其中第i个学生学习Ai分钟后睡眠Bi分钟,依次重复.其中在从清醒到睡眠时有一个条件:只有到全班睡眠人数大于清醒人 ...

  8. Android webview 写入cookie的解决方法以及一些属性设置

    原文地址:https://www.2cto.com/kf/201703/616868.html Android webview 写入cookie的解决方法以及一些属性设置,webview怎么设置写入C ...

  9. python初级 1 内存和变量

    一.回顾: 1.什么是程序 一堆指令的集合 2.回想一下猜数游戏程序的特征: 1)需要输入(input) 2)会处理输入(process) 3)产生输出(output) 二.程序的一般特征:输入.处理 ...

  10. LabWindows/CVI入门之第四章:库文件(转)

    按语: 在参考CVI参考书使用CVI生成动态库后,在另一工程中调用DLL ,编译通不过,后参考此文,豁然开朗. http://blog.sina.com.cn/s/blog_6373e9e60101b ...