实际开发中,我们经常会接触到生产消费者模型,如: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. Nyoj 修路方案(次小生成树)

    描述 南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路. 现在已经知道哪些城市之间可以修路,如果修路,花费是多少. 现在,军师小工已经找到 ...

  2. Max Sum(最大子序和)

    Description Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum of a sub ...

  3. linux_根据关键词_路径下递归查找code

    1:进入想查找的项目根目录 2:根据关键词查找 find . -name "*" |xargs grep -F '10.26'

  4. elasticsearch的rest搜索--- 安装

    目录: 一.针对这次装B 的解释 二.下载,安装插件elasticsearch-1.7.0   三.索引的mapping 四. 查询 五.对于相关度的大牛的文档 二.安装   1. 安装head管理插 ...

  5. JDBC加载过程

    jdbc载入的过程如图所看到的. 桥接模式请參照:设计模式:桥接模式 blog目的:与图说话 版权声明:本文博客原创文章,博客,未经同意,不得转载.

  6. 【百度地图API】自行获取区域经纬度的工具

    原文:[百度地图API]自行获取区域经纬度的工具 摘要:上一章教大家如何建立自己的行政区域地图.这次为大家提供一个,可视化选择区域,并且能自动生成经纬度代码的工具.工具的源代码完全公开,并且做了详尽的 ...

  7. Linux开发环境的搭建和使用——Linux本必备软件SSH

    SSH 至 Secure Shell 缩写.由 IETF 网络工作组(Network Working Group)开发:SSH 以建立应用层和传输层安全协议中的基础上. SSH 是眼下较可靠,专为远程 ...

  8. Host和Server的开发

    Host和Server的开发 对于开发人员来说,代码就是最好的文档,如上一篇博文所说,下面我们就会基于Kanata项目的一些具体调用代码,来进一步深入理解OWIN的实现和作用. 今天我们先针对Host ...

  9. 怎样才能充分利用SQL索引

    原文:怎样才能充分利用SQL索引 背景:目前WEB的普及太快,很多网站都会因为大流量的数据而发生服务器习惯性死机,一个查询语句只能适用于一定的网络环境.没有优化的查询当遇上大数据量时就不适用了. 本文 ...

  10. sql server drop talbe 自动删除关联的外键 ,权限体系(一)

    if object_id('Proc_DropTableWithFK') is not null begin drop proc dbo.Proc_DropTableWithFK end GO ) a ...