实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程、数据缓冲区在文件读写应用等。强大的模型框架,鉴于本人水平有限目前水平只能膜拜,本次只能算学习笔记,为了巩固自己对Java多线程常规知识点的理解,路过大神还望能指导指导。下面一段代码是最常规的生产者消费者的例子:

package com.zhanglei.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random; public class ResourceBuffer { private final int DEFAULT_BUFFER_SIZE = 100;
private int size;
private Random rnd;
private List<Integer> bufferList = new ArrayList<Integer>();
public ResourceBuffer(int size){
rnd = new Random();
if(size >0)
this.size = size;
else
this.size = DEFAULT_BUFFER_SIZE;
} public synchronized void product(){
if(bufferList.size() == size){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int num = rnd.nextInt(100);
bufferList.add(num);
System.out.println("生产商品编号"+num);
notifyAll();
} public synchronized void consumer(){
if(bufferList.size() == 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int index = bufferList.size() -1;
System.out.println("消费商品编号"+bufferList.get(index));
bufferList.remove(index);
notifyAll();
}
}
package com.zhanglei.demo;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Program { /**
* @param args
*/
public static void main(String[] args) {
ResourceBuffer buffer = new ResourceBuffer(10);
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new ProductTask(buffer));
executor.execute(new ConsumerTask(buffer));
}
} class ConsumerTask implements Runnable{ private ResourceBuffer buffer;
public ConsumerTask(ResourceBuffer buffer){
this.buffer = buffer;
} @Override
public void run() {
while(true){
buffer.consumer();
}
}
} class ProductTask implements Runnable{
private ResourceBuffer buffer;
public ProductTask(ResourceBuffer buffer){
this.buffer = buffer;
} @Override
public void run() {
while(true){
buffer.product();
}
}
}

以上代码通过实现对ResourceBuffer类的对象生产和消费来实现同步和协作,实际上就是对资源互斥访问实现同步。我们同样可以用java.util.concurrent包下的Lock接口实现同样的效果,代码如下:

package com.zhanglei.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class ResourceBuffer { private final int DEFAULT_BUFFER_SIZE = 100;
private int size;
private Random rnd;
private List<Integer> bufferList = new ArrayList<Integer>();
private Lock lock = new ReentrantLock();
private Condition notEmpty = lock.newCondition();//不为空条件
private Condition notFill = lock.newCondition();//不为满条件
public ResourceBuffer(int size){
rnd = new Random();
if(size >0)
this.size = size;
else
this.size = DEFAULT_BUFFER_SIZE;
} public void product(){
lock.lock();
try{
if(bufferList.size() == size){
try {
notFill.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int num = rnd.nextInt(100);
bufferList.add(num);
System.out.println("生产商品编号"+num);
notEmpty.signalAll();
}
finally{
lock.unlock();
}
} public void consumer(){
lock.lock();
try{
if(bufferList.size() == 0){
try {
notEmpty.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
int index = bufferList.size() -1;
System.out.println("消费商品编号"+bufferList.get(index));
bufferList.remove(index);
notFill.signalAll();
}
finally{
lock.unlock();
}
}
}

  通过以上代码实现的对生产者和消费者模式的同步,也只是实现对资源互斥访问实现同步,这种同步方式的并发并不高。如果说这种方式的生产者和消费者模式有什么优势的话,我个人觉得唯一的优势,即使发生了异常,也能保证锁一定能被释放。这种方式只是解决了同步问题,还有并发还有提高的空间。我们通过同步方法,我们本来目的只是为了保证生产和消费互斥操作,但是我们本来可以多个生产者一起生产的情况也被禁止了,这样让我们的并发度降低不少。

  由此,我们可以改进我们的生产者和消费者模式,下面我们通过引入读写锁来解决不能多个生产者同时生产或者多个消费者同时消费的问题。改进后的代码如下:

package com.zhanglei.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; public class ResourceBuffer {
private final int DEFAULT_BUFFER_SIZE = 100;
private int size;
private Random rnd;
private List<Integer> bufferList = new ArrayList<Integer>();
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
public ResourceBuffer(int size){
rnd = new Random();
if(size >0)
this.size = size;
else
this.size = DEFAULT_BUFFER_SIZE;
} public void product(){
rwLock.writeLock().lock();
try{
if(bufferList.size() == size){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int num = rnd.nextInt(100);
bufferList.add(num);
System.out.println("生产商品编号"+num);
}
finally{
rwLock.writeLock().unlock();
}
} public void consumer(){
rwLock.readLock().lock();
try{
if(bufferList.size() == 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int index = bufferList.size() -1;
System.out.println("消费商品编号"+bufferList.get(index));
bufferList.remove(index);
}
finally{
rwLock.readLock().unlock();
}
}
}

本着不重复造轮子的原则,生产者和消费者模式中的缓存区,在我们java类库已经做了相当好的封装,我们下面引入java.util.concurrent下的ArrayBlockingQueue来实现我们的生产者和消费者模式的代码如下:

  

package com.zhanglei.demo;

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue; public class ResourceBuffer {
private final int DEFAULT_BUFFER_SIZE = 100;
private int size;
private Random rnd;
private ArrayBlockingQueue<Integer> arrayQueue; public ResourceBuffer(int size){
if(size >0)
this.size = size;
else
this.size = DEFAULT_BUFFER_SIZE;
rnd = new Random();
arrayQueue = new ArrayBlockingQueue<Integer>(size);
//此处指定数组的队列的初始容量大小
} public void product() {
int num = rnd.nextInt(100);
System.out.println("生产商品编号"+num);
try {
arrayQueue.put(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
} public void consumer(){
int num;
try {
num = arrayQueue.take();
System.out.println("消费商品编号"+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

  我们通过查看put和take方法的源码,我们知道ArrayBlockingQueue已经实现我们以上可阻塞的队列。关于offer和poll的源码如下:

public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}

  通过ArrayBlockingQueue源码,我们看到ArrayBlockingQueue的读/取的方法跟以上生产者和消费者方法实现基本一致。

Java多线程学习笔记--生产消费者模式的更多相关文章

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

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

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

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

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

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

  4. 多线程学习之三生产者消费者模式Guarded Suspension

    Guarded Suspension[生产消费者模式] 一:guarded suspension的参与者--->guardedObject(被防卫)参与者                1.1该 ...

  5. Java多线程学习笔记

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

  6. Java 多线程学习笔记:生产者消费者问题

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

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

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

  8. Celery 框架学习笔记(生产者消费者模式)

    生产者消费者模式 在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产 ...

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

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

随机推荐

  1. poj 1061青蛙的约会

    青蛙的约会 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 90083   Accepted: 16257 Descripti ...

  2. 大数据系列修炼-Scala课程07

    由于昨天下班后有点困,就没有来及写博客,今天会把它补上!把这个习惯坚持下去! 关于Scala高阶函数详解 1.Scala高阶函数代码实现:高阶函数就是在我们函数中套用函数 2.高阶函数代码详解:高阶函 ...

  3. python_基础学习_02_拆分文件(spilt)

    做爬虫经常会有这样的引用场景 ,原始网页存储格式为  url+\t+ html php 有个explode的 拆分文本行方法,比较方便直接接收列值 list($url,$html)=explode(& ...

  4. 从头开始学JavaScript (三)——数据类型

    原文:从头开始学JavaScript (三)--数据类型 一.分类 基本数据类型:undefined.null.string.Boolean.number 复杂数据类型:object object的属 ...

  5. c# WebBrowser开发参考资料

    原文:c# WebBrowser开发参考资料 c# WebBrowser开发参考资料,所有资料的采集均来自网上 话说有了WebBrowser类,终于不用自己手动封装SHDocVw的AxWebBrows ...

  6. Cassandra C++/NodeJs开发环境

    工作的需要,开始更多地倾向于去中心化的结构,目前看来Cassandra算是去中心化DB中性能/管理最热门的选择,崇尚其P2P的理念. 自身原因对JAVA不擅长(周围写C的好少),还是更热衷于C++/J ...

  7. java设计模式之五原型模式(Prototype)

    原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.本小结会通过对象的复制,进行讲解.在Java中 ...

  8. ios基金会-XCode温馨提示

    (一个)代号规格pragma mark 1.定义 #pragma 开头的代码是一条编译器指令,是一个特定于程序或编译器的指令. 不一定适用于其他编译器或其他环境.假设编译器不能识别该指令.则会将其忽略 ...

  9. 一个完整的Installshield安装程序实例—艾泽拉斯之海洋女神出品(四) --高级设置二

    原文:一个完整的Installshield安装程序实例-艾泽拉斯之海洋女神出品(四) --高级设置二 上一篇:一个完整的安装程序实例—艾泽拉斯之海洋女神出品(三) --高级设置一4. 根据用户选择的组 ...

  10. VS2015预览

    VS2015预览版体验   .NET开源了,JAVA颤抖吧...据说VS2015可以开发android,ios,wp应用程序了,还可以开发能运行在mac,linux上的ASP.NET网站,如果真是这样 ...