前言:最近在学习Java多线程,看到ImportNew网上有网友翻译的一篇文章《阻塞队列实现生产者消费者模式》。在文中,使用的是Java的concurrent包中的阻塞队列来实现。在看完后,自行实现阻塞队列。

(一)准备

  在本博文中,没有使用concurrent包中提供的阻塞队列,而是基于最近对多线程的学习,使用了ReentrantLock,开发实现了阻塞队列。如果想参考concurrent包中阻塞队列的使用方式,请点击上面的连接。

  在多线程中,生产者-消费者问题是一个经典的多线程同步问题。简单来说就是有两种线程(在这里也可以做进程理解)——生产者和消费者,他们共享一个固定大小的缓存区(如一个队列)。生产者负责产生放入新数据,消费者负责取出缓存区的数据。具体介绍请参考 Producer-consumer problem

(二)设计概述

  详细的代码可以到我的 Github 仓库下载(master分支为详细的代码,输出所有的必要信息;simplification分支为简化的代码)。

  项目代码,分为四个类:Consumer类为消费者线程,Producer类为生产者线程,ProducerConsumer测试的启动类,ProducerConsumerQueue类为阻塞队列。

  在多线程中,生产者不断向队列放入新数据,当队列已满时,生产者将被阻塞,直到消费者从队列中取出部分数据并唤醒生产者;消费者不断从队列取出数据,当队列为空时,消费者将被阻塞,直到生产者再次放入新数据并唤醒消费者。在JAVA的concurrent包中,提供了一系列阻塞队列帮我们完成这一工作。这一工作也恰好是生产者消费者的问题的关键所在。在这里,我将自行实现阻塞队列:ProducerConsumerQueue;

(三)详细实现

  (1)ProducerConsumerQueue类

 public final class ProducerConsumerQueue<E> {
//声明一个重入锁
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
//缓存大小
private final int CAPACITY;
//存储数据的缓存队列
private Queue<E> queue; public ProducerConsumerQueue(int CAPACITY) {
this.CAPACITY = CAPACITY;
this.queue = new LinkedList<E>();
} //put the e into the queue
public void put(E e) throws InterruptedException {
lock.lock(); try {
while (queue.size() == this.CAPACITY)
this.notFull.await();
queue.offer(e);
this.notEmpty.signalAll();
} finally {
lock.unlock();
}
} //get e from queue.
public E take() throws InterruptedException {
lock.lock(); try {
while (queue.size() == 0)
this.notEmpty.await();
this.notFull.signalAll();
return queue.poll();
} finally {
lock.unlock();
}
}
}

  该队列提供put()和take()方法,前者用于生产者放入数据,后者用于消费者取出数据。

  在put方法中,首先要获得锁。然后检测是否队列已满,如果是,阻塞生产者线程,并释放锁;当再次获得锁时,这里使用了while语句,使得线程再次检查队列是否已满,如果未满,放入数据到缓存,并唤醒可能阻塞的消费者线程。在这里使用while,目的是防止其他生产者线程已经唤醒,并且成功放入新数据到队列,然后阻塞,而该线程又再次放入,导致可能缓存溢出。

  在take方法中,同样要先获得锁。检测队列是否已空,如果是,阻塞消费者线程,释放锁;当被唤醒并再次获得锁时,使用while再次检查队列是否仍为空,如果非空,则取出缓存数据。while同样为了防止其他消费者对一个空队列取数据。

  (2)Consumer类

 public class Consumer implements Runnable {
private final ProducerConsumerQueue sharedQueue; public Consumer(ProducerConsumerQueue sharedQueue) {
this.sharedQueue = sharedQueue;
} @Override
public void run() {
while (true) {
try {
synchronized (this){
System.out.println("\tConsumer: " + sharedQueue.take());
}
} catch (InterruptedException ex) {
System.out.println(Thread.currentThread().getName() + " throw a interrupexception.");
//do something.
}
}
}
}

  消费者线程持续从队列中取出数据。

  (3)Producer类

 public class Producer implements Runnable {
private final ProducerConsumerQueue sharedQueue; public Producer(ProducerConsumerQueue sharedQueue) {
this.sharedQueue = sharedQueue;
} @Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
synchronized (this){
System.out.println("Producer: "+i);
sharedQueue.put(i);
}
} catch (InterruptedException ex) {
System.out.println(Thread.currentThread().getName() + " throw a interrupexception. ");
//do something.
}
}
}
}

  生产者线程产生数据并放入队列中。

  (4)ProducerConsumer测试类

public class ProducerConsumer {
public static void main(String[] args) {
ProducerConsumerQueue sharedQueue = new ProducerConsumerQueue(4); Thread prodThread = new Thread(new Producer(sharedQueue));
Thread consThread1 = new Thread(new Consumer(sharedQueue));
Thread consThread2 = new Thread(new Consumer(sharedQueue)); prodThread.start();
consThread1.start();
consThread2.start();
}
}

  创建一个生产者线程和两个消费者线程。

扩展:

1、Wikipedia:http://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem

2、《聊聊并发——生产者和消费者模式》http://www.infoq.com/cn/articles/producers-and-consumers-mode

Java 多线程学习笔记:生产者消费者问题的更多相关文章

  1. Java多线程学习笔记--生产消费者模式

    实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...

  2. java多线程学习笔记——详细

    一.线程类  1.新建状态(New):新创建了一个线程对象.        2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

  3. JAVA多线程学习笔记(1)

    JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...

  4. Java多线程学习笔记(一)——多线程实现和安全问题

    1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...

  5. Java多线程学习笔记

    进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...

  6. Java多线程-并发协作(生产者消费者模型)

    对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一样,Hello World!都是最经典的例子. 实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓 ...

  7. JAVA多线程经典问题 -- 生产者 消费者

    工作2年多来一直也没有计划写自己的技术博客,最近辞职在家翻看<thingking in JAVA>,偶尔看到了生产者与消费者的一个经典的多线程同步问题.本人在工作中很少使用到多线程以及高并 ...

  8. java 多线程 22 :生产者/消费者模式 进阶 利用await()/signal()实现

    java多线程15 :wait()和notify() 的生产者/消费者模式 在这一章已经实现了  wait/notify 生产消费模型 利用await()/signal()实现生产者和消费者模型 一样 ...

  9. java多线程学习笔记(四)

    上一节讲到Synchronized关键字,synchronized上锁的区域:对象锁=方法锁/类锁 本节补充介绍一下synchronized锁重入: 关键字synchronized拥有锁重入的功能,也 ...

随机推荐

  1. elasticsearch-环境搭建

    1:下载并安装JDK 下载地址:jdk-8u91-windows-x64.exe 2:下载elasticsearch压缩包 下载地址:elasticsearch-2.3.0.zip 下载之后解压缩文件 ...

  2. redis for lack of backlog

    版本: redis-3.2.9 部署: 5台64G内存的物理机,每台机器启动2个redis进程组成5主5备集群,每台机器1个主1个备,并且错开互备. 问题: 发现redis进程占用内存高达40G,而且 ...

  3. DynamicJson-好用dotnet4的json对象

    最近看的书很杂,也深深被ES6的语法感动了,原来javascript可以这么好用.于是重新学习C#4.5.6,发现有一个dynamic就象是一个javascript对象,好玩!且有人写了一个json到 ...

  4. 20155326 《Java程序设计》实验五网络编程与安全实验报告

    20155326 <Java程序设计>实验五网络编程与安全实验报告 实验内容 任务一 1.两人一组结对编程: 参考http://www.cnblogs.com/rocedu/p/67667 ...

  5. noip第1课作业

    1.    求三个数的乘积和三次方和 [问题描述] 编程实现输入任意三个整数a, b, c,将这三个数的乘积以及三次方和输出: [样例输入] 1 2 3 [样例输出] 6 36 #include &l ...

  6. 关于建立MySQL数据库,中文出现乱码问题

    MySQL的ini文件中的默认编码设置utf-8不用改 loose-default-character-set = utf-8 注意:需要改动部分 1.如图所示 建立schema时改变字符编码 改变为 ...

  7. 12.equals()方法总结

    超类Object中有这个equals()方法,该方法主要用于比较两个对象是否相等.该方法的源码如下: 我们知道所有对象都有表示(内存地址)和状态(数据),看上面代码是用"=="来比 ...

  8. docker opencpu R

    有一个项目中用到了docker opencpu R,这里把所学的整理下. docker,一个运行容器,搭建一次,以后可以很方便的移植,win7 64下也可以装. opencpu,云端计算,运行R函数和 ...

  9. AngularJS transclude 理解及例子

    一.概念理解 transclude可以在指令中让使用者自定义模板,也就是说,指令中模板的一部分,让指令的使用者动态指定:与指定中的Scope属性值为{}时候的作用类似,scope属性让指令使用者动态制 ...

  10. HTML 基础 块级元素与行内元素

    块元素:单独占一行,宽度占整行,可以包含内联元素和其他块元素,通过样式display:inline,变为行内元素,不换行 内联元素:不单独占一行,宽度根据内容来决定,只能容纳文本或者其他内联元素 ,可 ...