实际开发中,我们经常会接触到生产消费者模型,如: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. 他们控制的定义--让背景颜色变化ViewPager逐步幻灯片

    转载请注明出处.谢谢~ 今天想说一个简单但很好的效果达到.代码是绝对简单,达到绝对easy,就是你可能想不到而已. 不多说,上效果图. 第一个效果是仿最美应用的滑动颜色变化,第二个是我项目中要用的效果 ...

  2. 浅谈JavaScript中typeof与instanceof的区别

      首先,我们从其常规定义入手:       instanceof 运算符可以用来判断某个构造函数的 prototype 属性是否存在另外一个要检测对象的原型链上.(需要注意的一点是:prototyp ...

  3. 杭电1162Eddy&#39;s picture

    Eddy's picture Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Tota ...

  4. ocp11g培训内部教材_053课堂笔记(043)_数据备份

    053:数据库高级管理: 目录 第一部分:数据库备份与恢复... 4 第一章:备份恢复概述... 4 1.1 备份的意义: 4 1.2 数据库故障的类型:... 4 1.3 制定你的备份和恢复的计划. ...

  5. 使用excel微调button调整日期

    笔者:iamlaosong excel提供了一个调整的数字button.用来调节单元格增加或减少数量.因为它需要值是0-30000.所以不能直接用其调节日期.但能够使用"初始日期+调节值&q ...

  6. 一个JavaWeb项目开发总结

    一.学会如何读一个JavaWeb项目源代码 步骤:表结构->web.xml->mvc->db->spring ioc->log->代码 先了解项目数据库的表结构,这 ...

  7. 使用Visual Studio创建映像向导(Image Sprite)——Web Essential

    原版的:Creating Image Sprite in Visual Studio - Web Essential 译者注:有关图片精灵的信息请參阅http://baike.baidu.com/vi ...

  8. 开发者:网站 & SDK

    { 收集的一些.开发工具 } Teambition 团队协作工具 GitCafe 代码托管 FIR.im App 托管平台 Coding 代码托管,项目管理,WebIDE 计蒜客 编程学习 SendC ...

  9. [CLR via C#]1.4 执行程序集的代码

    原文:[CLR via C#]1.4 执行程序集的代码 1. 托管程序集同时包含元数据和IL.IL是与CPU无关的机器语言.可将IL是为一种面向对象的机器语言. 2. IL也是能使用汇编语言来写的,M ...

  10. 多线程——@synchronized(object)

    @synchronized 的作用是创建一个相互排斥锁,保证此时没有其他线程对self对象进行改动.这个是objective-c的一个锁定令牌,防止self对象在同一时间内被其他线程訪问,起到线程的保 ...