引言

  生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间内共用同一个存储空间,如下图所示,生产者向空间里存放数据,而消费者取用数据,如果不加以协调可能会出现以下情况:

生产者消费者图

  存储空间已满,而生产者占用着它,消费者等着生产者让出空间从而去除产品,生产者等着消费者消费产品,从而向空间中添加产品。互相等待,从而发生死锁

  生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。解决生产者/消费者问题的方法可分为两类:

  (1)采用某种机制保护生产者和消费者之间的同步;

  (2)在生产者和消费者之间建立一个管道。

  第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。因此本文只介绍同步机制实现的生产者/消费者问题。

  同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持。在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。

(1)wait() / notify()方法

(2)await() / signal()方法

(3)BlockingQueue阻塞队列方法

(4)PipedInputStream / PipedOutputStream

本文只介绍最常用的前两种种,第三、四种暂不做讨论,有兴趣的读者可以自己去网上找答案。

一、wait() / notify()方法

  wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。

  wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。

  notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

代码实现:

1、仓库类

import java.util.LinkedList;  

/**
* 仓库类Storage实现缓冲区
*
* @author zcr
*/
public class Storage
{
// 仓库最大存储量
private final int MAX_SIZE = 100; // 仓库存储的载体
private LinkedList<Object> list = new LinkedList<Object>(); /**
* 生产num个产品
* @param num 生产产品的数量
*/
public void produce(int num)
{
// 同步代码段
synchronized (list)
{
// 如果仓库剩余容量不足
while (list.size() + num > MAX_SIZE)
{
System.out.println("【要生产的产品数量】:" + num + " \t 【库存量】:"
+ list.size() + "\t 暂时不能执行生产任务!");
try
{
// 由于条件不满足,生产阻塞
list.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} // 生产条件满足情况下,生产num个产品
for (int i = 1; i <= num; ++i)
{
list.add(new Object());
} System.out.println("【已经生产产品数】:" + num + "\t 【现仓储量为】:" + list.size()); list.notifyAll();
}
} /**
* 消费num个产品
* @param num 消费产品数量
*/
public void consume(int num)
{
// 同步代码段
synchronized (list)
{
// 如果仓库存储量不足
while (list.size() < num)
{
System.out.println("【要消费的产品数量】:" + num + " \t【库存量】:"
+ list.size() + " \t 暂时不能执行生产任务!");
try
{
// 由于条件不满足,消费阻塞
list.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} // 消费条件满足情况下,消费num个产品
for (int i = 1; i <= num; ++i)
{
list.remove();
} System.out.println("【已经消费产品数】:" + num + " \t 【现仓储量为】:" + list.size()); list.notifyAll();
}
} // get/set方法
public LinkedList<Object> getList()
{
return list;
} public void setList(LinkedList<Object> list)
{
this.list = list;
} public int getMAX_SIZE()
{
return MAX_SIZE;
}
}

2、生产者

/**
* 生产者类Producer继承线程类Thread
*
*
* @author zcr
*
*/
public class Producer extends Thread
{
// 每次生产的产品数量
private int num; // 所在放置的仓库
private Storage storage; // 构造函数,设置仓库
public Producer(Storage storage)
{
this.storage = storage;
} // 线程run函数
public void run()
{
produce(num);
} // 调用仓库Storage的生产函数
public void produce(int num)
{
storage.produce(num);
} // get/set方法
public int getNum()
{
return num;
} public void setNum(int num)
{
this.num = num;
} public Storage getStorage()
{
return storage;
} public void setStorage(Storage storage)
{
this.storage = storage;
}
}

3、消费者

/**
* 消费者类Consumer继承线程类Thread
*
*
* @author zcr
*
*/
public class Consumer extends Thread
{
// 每次消费的产品数量
private int num; // 所在放置的仓库
private Storage storage; // 构造函数,设置仓库
public Consumer(Storage storage)
{
this.storage = storage;
} // 线程run函数
public void run()
{
consume(num);
} // 调用仓库Storage的生产函数
public void consume(int num)
{
storage.consume(num);
} // get/set方法
public int getNum()
{
return num;
} public void setNum(int num)
{
this.num = num;
} public Storage getStorage()
{
return storage;
} public void setStorage(Storage storage)
{
this.storage = storage;
}
}

4、测试类

/**
* 测试类Test
* @author zcr
*
*/
public class Test
{
public static void main(String[] args)
{
// 仓库对象
Storage storage = new Storage(); // 生产者对象
Producer p1 = new Producer(storage);
Producer p2 = new Producer(storage);
Producer p3 = new Producer(storage);
Producer p4 = new Producer(storage);
Producer p5 = new Producer(storage);
Producer p6 = new Producer(storage);
Producer p7 = new Producer(storage); // 消费者对象
Consumer c1 = new Consumer(storage);
Consumer c2 = new Consumer(storage);
Consumer c3 = new Consumer(storage); // 设置生产者产品生产数量
p1.setNum(10);
p2.setNum(10);
p3.setNum(10);
p4.setNum(10);
p5.setNum(10);
p6.setNum(10);
p7.setNum(80); // 设置消费者产品消费数量
c1.setNum(50);
c2.setNum(20);
c3.setNum(30); // 线程开始执行
c1.start();
c2.start();
c3.start();
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
p6.start();
p7.start();
}
}

5、结果:

【要消费的产品数量】:50     【库存量】:0      暂时不能执行生产任务!
【要消费的产品数量】:20 【库存量】:0 暂时不能执行生产任务!
【已经生产产品数】:10 【现仓储量为】:10
【要消费的产品数量】:20 【库存量】:10 暂时不能执行生产任务!
【要消费的产品数量】:50 【库存量】:10 暂时不能执行生产任务!
【已经生产产品数】:10 【现仓储量为】:20
【已经生产产品数】:10 【现仓储量为】:30
【要消费的产品数量】:50 【库存量】:30 暂时不能执行生产任务!
【已经消费产品数】:20 【现仓储量为】:10
【已经生产产品数】:10 【现仓储量为】:20
【已经生产产品数】:10 【现仓储量为】:30
【要消费的产品数量】:50 【库存量】:30 暂时不能执行生产任务!
【已经生产产品数】:10 【现仓储量为】:40
【已经消费产品数】:30 【现仓储量为】:10
【要消费的产品数量】:50 【库存量】:10 暂时不能执行生产任务!
【已经生产产品数】:80 【现仓储量为】:90
【已经消费产品数】:50 【现仓储量为】:40

  看完上述代码,对wait() / notify()方法实现的同步有了了解。你可能会对Storage类中为什么要定义public void produce(int num);和public void consume(int num);方法感到不解,为什么不直接在生产者类Producer和消费者类Consumer中实现这两个方法,却要调用Storage类中的实现呢?淡定,后文会有解释。我们先往下走。


二、await() / signal()方法

  在JDK5.0之后,Java提供了更加健壮的线程处理机制,包括同步、锁定、线程池等,它们可以实现更细粒度的线程控制。await()和signal()就是其中用来做同步的两种方法,它们的功能基本上和wait() / nofity()相同,完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。通过在Lock对象上调用newCondition()方法,将条件变量和一个锁对象进行绑定,进而控制并发程序访问竞争资源的安全

  下面来看代码:  

  只需更新仓库类Storage的代码即可,生产者Producer、消费者Consumer、测试类Test的代码均不需要进行任何更改。

仓库类

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* 仓库类Storage实现缓冲区
*
*
* @author MONKEY.D.MENG 2011-03-15
*
*/
public class Storage
{
// 仓库最大存储量
private final int MAX_SIZE = 100; // 仓库存储的载体
private LinkedList<Object> list = new LinkedList<Object>(); // 锁
private final Lock lock = new ReentrantLock(); // 仓库满的条件变量
private final Condition full = lock.newCondition(); // 仓库空的条件变量
private final Condition empty = lock.newCondition(); // 生产num个产品
public void produce(int num)
{
// 获得锁
lock.lock(); // 如果仓库剩余容量不足
while (list.size() + num > MAX_SIZE)
{
System.out.println("【要生产的产品数量】:" + num + "/t【库存量】:" + list.size()
+ "/t暂时不能执行生产任务!");
try
{
// 由于条件不满足,生产阻塞
full.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} // 生产条件满足情况下,生产num个产品
for (int i = 1; i <= num; ++i)
{
list.add(new Object());
} System.out.println("【已经生产产品数】:" + num + "/t【现仓储量为】:" + list.size()); // 唤醒其他所有线程
full.signalAll();
empty.signalAll(); // 释放锁
lock.unlock();
} // 消费num个产品
public void consume(int num)
{
// 获得锁
lock.lock(); // 如果仓库存储量不足
while (list.size() < num)
{
System.out.println("【要消费的产品数量】:" + num + "/t【库存量】:" + list.size()
+ "/t暂时不能执行生产任务!");
try
{
// 由于条件不满足,消费阻塞
empty.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} // 消费条件满足情况下,消费num个产品
for (int i = 1; i <= num; ++i)
{
list.remove();
} System.out.println("【已经消费产品数】:" + num + "/t【现仓储量为】:" + list.size()); // 唤醒其他所有线程
full.signalAll();
empty.signalAll(); // 释放锁
lock.unlock();
} // set/get方法
public int getMAX_SIZE()
{
return MAX_SIZE;
} public LinkedList<Object> getList()
{
return list;
} public void setList(LinkedList<Object> list)
{
this.list = list;
}
}

结果:

【要消费的产品数量】:50/t【库存量】:0/t暂时不能执行生产任务!
【已经生产产品数】:10/t【现仓储量为】:10
【已经生产产品数】:10/t【现仓储量为】:20
【要消费的产品数量】:30/t【库存量】:20/t暂时不能执行生产任务!
【已经消费产品数】:20/t【现仓储量为】:0
【已经生产产品数】:10/t【现仓储量为】:10
【已经生产产品数】:10/t【现仓储量为】:20
【要消费的产品数量】:50/t【库存量】:20/t暂时不能执行生产任务!
【已经生产产品数】:10/t【现仓储量为】:30
【已经生产产品数】:10/t【现仓储量为】:40
【要生产的产品数量】:80/t【库存量】:40/t暂时不能执行生产任务!
【已经消费产品数】:30/t【现仓储量为】:10
【要消费的产品数量】:50/t【库存量】:10/t暂时不能执行生产任务!
【已经生产产品数】:80/t【现仓储量为】:90
【已经消费产品数】:50/t【现仓储量为】:40

  

  这样我们就知道为神马我要在Storage类中定义public void produce(int num);和public void consume(int num);方法,并在生产者类Producer和消费者类Consumer中调用Storage类中的实现了吧。将可能发生的变化集中到一个类中,不影响原有的构架设计,同时无需修改其他业务层代码。


总结

  两种方式原理一致,都是对独占空间加锁,阻塞和唤醒线程,第一种方式比较传统,第二种方式速度比较快。

http://www.cnblogs.com/0201zcr/p/4758533.html

java 生产者消费者问题 并发问题的解决(转)的更多相关文章

  1. java 生产者消费者问题 并发问题的解决

    引言 生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,如下图所示,生产者向空间里存放数据,而消费者取用数据,如果不加以协调可能会出现以下情况: 生产者消费者图 ...

  2. 基于Java 生产者消费者模式(详细分析)

    Java 生产者消费者模式详细分析 本文目录:1.等待.唤醒机制的原理2.Lock和Condition3.单生产者单消费者模式4.使用Lock和Condition实现单生产单消费模式5.多生产多消费模 ...

  3. 基于异步队列的生产者消费者C#并发设计

    继上文<<基于阻塞队列的生产者消费者C#并发设计>>的并发队列版本的并发设计,原文code是基于<<.Net中的并行编程-4.实现高性能异步队列>>修改 ...

  4. Java生产者消费者的三种实现

    Java生产者消费者是最基础的线程同步问题,java岗面试中还是很容易遇到的,之前没写过多线程的代码,面试中被问到很尬啊,面完回来恶补下.在网上查到大概有5种生产者消费者的写法,分别如下. 用sync ...

  5. Java 生产者消费者模式详细分析

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  6. Java生产者消费者模式

    为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能 ...

  7. java生产者/消费者模式实现——一生产者一消费者(操作值)

    胶多不粘话多不甜,直接上代码: 生产者类: /** * Created by 51304 on 2016/2/28. */ public class P { private String lock; ...

  8. Java生产者消费者模型

    在Java中线程同步的经典案例,不同线程对同一个对象同时进行多线程操作,为了保持线程安全,数据结果要是我们期望的结果. 生产者-消费者模型可以很好的解释这个现象:对于公共数据data,初始值为0,多个 ...

  9. Java生产者消费者问题

    1. package interview.thread; import java.util.LinkedList; import java.util.Queue; import org.apache. ...

随机推荐

  1. uvalive 2911 Maximum(贪心)

    题目连接:2911 - Maximum 题目大意:给出m, p, a, b,然后xi满足题目中的两个公式, 要求求的 xp1 + xp2 +...+ xpm 的最大值. 解题思路:可以将x1 + x2 ...

  2. LeetCode18:4Sum

    Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...

  3. ORACLE 中的 锁 介绍

    ORACLE 中的 锁 介绍 Oracle数据库支持多个用户同时与数据库进行交互,每个用户都可以同时运行自己的事务,从而也需要对并发访问进行控制.Oracle也是用“锁”的机制来防止各个事务之间的相互 ...

  4. Windows Phone开发(15):资源

    原文:Windows Phone开发(15):资源 活字印刷术是我国"四大发明"之一,毕昇在发明活字印刷术之后,他很快发现一个问题,随着要印刷资料的不断增加,要用到的汉字数目越来越 ...

  5. 谈谈Linux内存释放

    上上周吧,一个朋友问我说他公司的服务器内存free 为0 是为什么,意思大概是内存去哪了,这引发了一个小小的讨论,也就是内存释放的问题… 首先我们可能会用free 去查看内存的使用率,它应该是这样的 ...

  6. T-SQL基础(2) - 单表查询

    开窗函数over select orderid, custid, val, SUM(val) over() as totalvalue, SUM(val) over(partition by cust ...

  7. Java正则表达式例子汇总

    1.过滤特殊字符 package com.sheepmu.text; /* * @author sheepmu */ public class HWCompetition { public stati ...

  8. ZOJ - 3822 Domination (DP)

    Edward is the headmaster of Marjar University. He is enthusiastic about chess and often plays chess ...

  9. React Native是一套使用 React 构建 Native app 的编程框架

    React Native是一套使用 React 构建 Native app 的编程框架 React Native at first sight what is React Native? 跟据官方的描 ...

  10. oracle物化视图使用+hibernate

    使用过程 ----删除 TRUNCATE TABLE mlog$_xxx_lxz_tmp;DROP MATERIALIZED VIEW LOG ON xxx_lxz_tmp; drop materia ...