生产者消费者模式:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据。生产者生产一个,消费者消费一个,不断循环。

第一种实现方法,用BlockingQueue阻塞队列来实现

LinkedBlockingQueue和ArrayBlockingQueue这两个类都实现了接口BlockingQueue,我们可以用这两个阻塞队列来处理多线程间的生产者消费者问题。

1.LinkedBlockingQueue:

基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。LinkedBlockingQueue是线程安全的。

2. ArrayBlockingQueue
  基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。
  ArrayBlockingQueue在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。Doug Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。而在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。

主要方法:

  • put(E e): 这个方法用于向BlockingQueue中插入元素,如果BlockingQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里有空间再继续。
  • E take(): 这个方法用于取走BlockingQueue里面排在首位的对象,如果BlockingQueue为空,则调用线程被阻塞,进入等待状态,直到BlockingQueue有新的数据被加入。

实现生产者消费者问题

ConsumerQueue.java 消费者类

public class ConsumerQueue implements Runnable {

    private final BlockingQueue conQueue;

    public ConsumerQueue(BlockingQueue conQueue) {
this.conQueue = conQueue;
} @Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
System.out.println("消费者消费的商品编号为 :" + conQueue.take());
Thread.sleep(300); // 在这里sleep是为了看的更加清楚些 } catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
} }
}
}

ProducerQueue.java 生产者类

public class ProducerQueue implements Runnable {

    private final BlockingQueue proQueue;

    public ProducerQueue(BlockingQueue proQueue) {
this.proQueue = proQueue;
} int task = 1; @Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
proQueue.put(task);
System.out.println("生产者生产的商品编号为 : " + task);
task++;
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}

SharedQueue.java 启动

public class SharedQueue {
public static void main(String[] args) {

     /*
      * 1.ArrayBlockingQueue必须指定队列大小,是有界的
      * 2.LinkedBlockingQueue可以不指定队列大小,无界,默认大小为Integer
      * .MAX_VALUE;也可以指定队列大小,变成有界的
      */

      // BlockingQueue blockingQueue = new ArrayBlockingQueue(10);

        BlockingQueue sharedQueue = new LinkedBlockingQueue(2); // 定义了一个大小为2的队列

        Thread pro = new Thread(new ProducerQueue(sharedQueue));
Thread con = new Thread(new ConsumerQueue(sharedQueue)); pro.start();
con.start();
} }
第二种:通过java提供的等待唤醒机制来解决

多线程常用函数:

1、线程睡眠:Thread.sleep(long millis)方法,使线程转到堵塞状态。



millis参数设定睡眠的时间。以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。



2、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其它线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。



3、线程让步:Thread.yield() 方法。暂停当前正在执行的线程对象。把执行机会让给同样或者更高优先级的线程。



4、线程添加:join()方法。等待其它线程终止。



在当前线程中调用还有一个线程的join()方法,则当前线程转入堵塞状态。直到还有一个进程执行结束,当前线程再由堵塞转为就绪状态。



5、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。假设全部线程都在此对象上等待。则会选择唤醒当中一个线程。选择是随意性的。并在对实现做出决定时发生。线程通过调用当中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定。才会继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其它全部线程进行竞争;比如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。相似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的全部线程。
 
getThread.java 消费者类
public class getThread implements Runnable {

    private Student student;

    public getThread(Student student) {
this.student = student;
} @Override
public void run() {
// TODO Auto-generated method stub
while (true) {
synchronized (student) {
// 消费者没用数据就等待
while (!student.flag) {
try {
student.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(student.name + "------" + student.age);
// 消费完了就置为false没有
student.flag = false;
student.notify();
}
}
} }

setThread.java 生产类

public class setThread implements Runnable {

    private Student student;
private int x = 0; public setThread(Student student) {
this.student = student;
} @Override
public void run() {
// TODO Auto-generated method stub
while (true) {
synchronized (student) {
// 生产者有数据就等待,修改为while,保证每次wait()后再notify()时先再次判断标记。
while(student.flag){
try {
student.wait(); // 等待,会同时释放锁;将来醒过来的时候,就是在这里醒过来的。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (x % 2 == 0) {
student.name = "AAA";
student.age = 22;
} else {
student.name = "BBB";
student.age = 24;
}
x++; //修改标记
student.flag = true;
student.notify();
}
}
} }

学生资源类

public class Student {
//同一个包下可以访问
String name;
int age;
boolean flag; // 默认情况是没有数据,如果有就是true
}

Main

public class StudentDemo {
public static void main(String[] args){
Student student = new Student();
setThread st = new setThread(student);
getThread gt = new getThread(student); Thread t1 = new Thread(st);
Thread t2 = new Thread(gt); t1.start();
t2.start();
} }

运行结果:

按顺序依次输出

 

Java实现多线程生产者消费者模式的两种方法的更多相关文章

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

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

  2. java 多线程并发系列之 生产者消费者模式的两种实现

    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据 ...

  3. java实现多线程生产者消费者模式

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

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

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

  5. Java构造和解析Json数据的两种方法详解二

    在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面接着介绍用org.json构造和解析Jso ...

  6. Java构造和解析Json数据的两种方法详解二——org.json

    转自:http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/24/3096437.html 在www.json.org上公布了很多JAVA下的jso ...

  7. Java构造和解析Json数据的两种方法详解一——json-lib

    转自:http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/23/3096001.html 在www.json.org上公布了很多JAVA下的jso ...

  8. Java执行shell脚本并返回结果两种方法的完整代码

    Java执行shell脚本并返回结果两种方法的完整代码 简单的是直接传入String字符串,这种不能执行echo 或者需要调用其他进程的命令(比如调用postfix发送邮件命令就不起作用) 执行复杂的 ...

  9. DES加密 java与.net可以相互加密解密两种方法

    DES加密 java与.net可以相互加密解密两种方法 https://www.cnblogs.com/DrWang/archive/2011/03/30/2000124.html sun.misc. ...

随机推荐

  1. Nacos笔记01——使用Nacos作为SpringCloud项目的服务注册中心

    前言 刚学SpringCloud时使用eureka作为服务注册中心,随着网飞公司eureka2.x不再更新,以及最近在公司实习接触到的SpringCloud项目是使用Nacos来做服务注册中心的,所以 ...

  2. switch语句中 参数的类型

    switch可作用于char byte short int switch可作用于char byte short int对应的包装类 switch不可作用于long double float boole ...

  3. Java 之 字符输入流[Reader]

    一.字符输入流 java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中. 它定义了字符输入流的基本共性功能方法. public void close() :关 ...

  4. Javascript的异步与单线程

    一.前言 我们都知道,javasript是一个单线程的语言:所谓单线程就是同一时间不能做两件事情,两段代码不能同时执行:因为这种机制,才避免了两段js同时对一个DOM节点进行渲染的冲突.但是也会因此产 ...

  5. Android笔记(十) Android中的布局——表格布局

    TableLayout运行我们使用表格的方式来排列控件,它的本质依然是线性布局.表格布局采用行.列的形式来管理控件,TableLayout并不需要明确的声明包含多少行多少列,而是通过添加TableRo ...

  6. JAVA笔记整理(七),JAVA几个关键字

    本篇主要总结JAVA中的super.this.final.static.break.continue 1.super super主要用在继承当中,表示调用父类的构造函数. 1.如果要在子类方法中调用父 ...

  7. Linux三剑客及使用介绍

    Linux 三剑客是(grep,sed,awk)三者的简称,熟练使用这三个工具可以提升运维效率.Linux 三剑客以正则表达式作为基础,而在Linux系统中,支持两种正则表达式,分别为"标准 ...

  8. 运输层2——用户数据报协议UDP

    目录 1. UDP概述 2. UDP首部格式 3. UDP首部检验和计算方法 写在前面:本文章是针对<计算机网络第七版>的学习笔记 运输层1--运输层协议概述 运输层2--用户数据报协议U ...

  9. linux网络编程之posix消息队列

    在前面已经学习了System v相关的IPC,今天起学习posix相关的IPC,关于这两者的内容区别,简单回顾一下: 而今天先学习posix的消息队列,下面开始: 接下来则编写程序来创建一个posix ...

  10. git 在不同服务器主机上同步 git 仓库

    git 在不同服务器主机上同步 git 仓库 参考链接:https://opentechguides.com/how-to/article/git/177/git-sync-repos.html 1. ...