《多线程操作之生产者消费者》(单生产单消费&多生产多消费)
说明1:假设有一个放商品的盘子(此盘子只能放下一个商品)。生产者每次生产一个商品之后,放到这个盘子里,然后唤醒消费者来消费这个面包。消费者消费完这个商品之后,就唤醒生产者生产下一个商品。前提是,只有盘子里没有商品时,生产者才生产商品,只有盘子里有商品时,消费者才来消费。因此第一个程序是一个“单生产” “单消费” 的问题。代码如下所示:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; //描述资源
//资源属性:商品名称和编号
//行为:对商品名称赋值,获取商品
class Resource{ private String name;
private int count = 1; // 定义标记
private boolean flag = false; //初始标记为假,表明盘子里没有商品(面包) // 定义一个锁对象
private Lock lock = new ReentrantLock(); //获取锁上的Condition对象
private Condition producer = lock.newCondition();//负责生产
private Condition consumer = lock.newCondition();//负责消费 // 提供生产商品的方法
public void set(String name) {
lock.lock(); //获取锁
try {
while (flag) //当flag标记为真时,说明盘子里有商品(面包)此时生产者等待,否则,生产商品
try {
producer.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName() + "...生产者..." + this.name);
flag = true;
// 唤醒一个消费者
consumer.signal();
} finally {//释放锁
lock.unlock();
}
} //提供消费的方法
public void out() {
lock.lock(); //获取锁
try {
while (!flag) //当flag标记为假时,说明盘子里没有商品(面包),此时,消费者等待,否则,消费商品(面包)
try {
consumer.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "...消费者..." + this.name);
flag = false;
//唤醒一个生产者
producer.signal();
} finally {//释放锁
lock.unlock();
}
}
} // 描述生产者
class Producer implements Runnable {
// 生产者一初始化就要有资源
private Resource r; public Producer(Resource r) {
this.r = r;
} @Override
public void run() {
while (true) {
r.set("面包");
}
}
} // 描述消费者
class Consumer implements Runnable {
// 消费者一初始化就要有资源
private Resource r; public Consumer(Resource r) {
this.r = r;
} @Override
public void run() {
while (true) {
r.out();
}
}
} public class ProducerConsumer {
public static void main(String[] args) {
// 创建资源对象
Resource r = new Resource(); // 创建线程任务
Producer pro = new Producer(r);
Consumer con = new Consumer(r); // 创建线程对象(两个生产者,两个消费者)
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con); // 开启线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果如下图所示:(注意Ctrl+C结束程序)

说明2:在多生产多消费问题中,我们假设有很多个盘子组成一个数组,生产者不停的生产商品(面包)往数组里面放,消费者不停的消费。当生产者判断已经所有盘子里都已经有面包时【注意,此处生产者判断所有盘子里都有面包,不是简单的判断生产的面包数目等于数组的长度这么简单,因为生产者在生产面包的同时,消费者也在消费面包,当生产者把生产的面包放到最后一个盘子里时,可能消费者已经消费了前面若干个面包了,所以此时并不满足所有盘子里都有面包。】生产者等待,唤醒一个消费者来消费。当消费者判断所有盘子里都没有面包时【注意:此处也不是简单的判断消费的面包数目等于数组长度这么简单,和上面的分析同理】消费者等待,唤醒一个生产者进行生产。多生产多消费的代码如下:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class BoundedBuffer {
final Lock lock = new ReentrantLock();//锁
final Condition notFull = lock.newCondition(); //生产
final Condition notEmpty = lock.newCondition(); //消费 final Object[] items = new Object[100];//存储商品的容器。
int putptr/*生产者使用的角标*/, takeptr/*消费者使用的角标*/, count/*计数器*/; /*生产者使用的方法,往数组中存储商品*/
public void put(Object x) throws InterruptedException {
lock.lock(); //获取锁
try {
while (count == items.length) //判断计数器是否已到数组长度。满了。
notFull.await();//生产就等待。 items[putptr] = x; //按照角标将商品存储到数组中
System.out.println(Thread.currentThread().getName()+"...生产者..."+items[putptr]+"--->"+count); if (++putptr == items.length) //如果存储的角标到了数组的长度,就将角标归零。
putptr = 0;
++count;//计数器自增。
notEmpty.signal();//唤醒一个消费者
} finally {
lock.unlock();
}
} //消费者使用的方法
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) //如果计数器为0,说明没有商品,消费者等待。
notEmpty.await();
Object x = items[takeptr]; //从数组中通过消费者角标获取商品。 if (++takeptr == items.length) //如果消费的角标等于了数组的长度,将角标归零。
takeptr = 0;
--count;//计数器自减。
System.out.println(Thread.currentThread().getName()+"...消费者--->"+items[takeptr]+"..."+count);
notFull.signal();//唤醒生产者。
return x;
} finally {
lock.unlock();
}
}
} //生产者
class Producer implements Runnable
{
//private int n = 1;
private BoundedBuffer b;
public Producer(BoundedBuffer b){
this.b = b;
} public void run(){
while(true){
try{
b.put("面包");
//n++;
}catch(InterruptedException e){} }
}
} //消费者
class Consumer implements Runnable
{
private BoundedBuffer b;
public Consumer(BoundedBuffer b){
this.b = b;
} public void run(){
while(true){
try{
b.take();
}catch(InterruptedException e){} }
}
} //主函数
public class ThreadDemo12
{
public static void main(String args[]){
//创建资源对象
BoundedBuffer b = new BoundedBuffer(); //创建线程任务
Producer pro = new Producer(b);
Consumer con = new Consumer(b); //创建线程对象
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con); //开启线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行截图如下:

欢迎留言交流!
《多线程操作之生产者消费者》(单生产单消费&多生产多消费)的更多相关文章
- 操作系统实验——PV操作实现生产者消费者模型
		
操作系统PV操作之--生产者消费者模型 个人博客主页 参考资料: Java实现PV操作 | 生产者与消费者 浙大公开课 在操作系统的多进程.多线程操作中经常会有因为同步.互斥等等问题引发出的一系列问题 ...
 - java线程基础巩固---多线程下的生产者消费者模型,以及详细介绍notifyAll方法
		
在上一次[http://www.cnblogs.com/webor2006/p/8419565.html]中演示了多Product多Consumer假死的情况,这次解决假死的情况来实现一个真正的多线程 ...
 - 再谈多线程模型之生产者消费者(总结)(c++11实现)
		
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
 - 再谈多线程模型之生产者消费者(多生产者和多消费者 )(c++11实现)
		
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
 - 再谈多线程模型之生产者消费者(多生产者和单一消费者 )(c++11实现)
		
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
 - 再谈多线程模型之生产者消费者(单一生产者和多消费者  )(c++11实现)
		
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
 - 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)
		
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)[本文] 再谈多线程模型之生 ...
 - 再谈多线程模型之生产者消费者(基础概念)(c++11实现)
		
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现)[本文] 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生 ...
 - java 多线程 22 :生产者/消费者模式 进阶 利用await()/signal()实现
		
java多线程15 :wait()和notify() 的生产者/消费者模式 在这一章已经实现了 wait/notify 生产消费模型 利用await()/signal()实现生产者和消费者模型 一样 ...
 
随机推荐
- 在CentOS6.9上Shell脚本定时释放内存cache
			
一.写Shell脚本 mkdir -p /var/script/ vim /var/script/freemem.sh 写入以下Shell脚本: #!/bin/bash # 当前已使用的内存大小 us ...
 - vim编辑器-多行加注释与去注释
			
在使用vim编辑器时,有时候需要对多行代码进行加注释或去注释,下面将介绍两种方法. 方法一:块选择模式 1. 插入注释 (1)首先按键盘上的ESC进入命令行模式 (2)再按Ctrl+V进入VISUAL ...
 - MySQL存储过程  CASE语句
			
MySQL存储过程 CASE语句 除了IF语句,MySQL提供了一个替代的条件语句CASE. MySQL CASE语句使代码更加可读和高效. CASE语句有两种形式:简单的搜索CASE语句. 简单C ...
 - 2018-2019-2 网络对抗技术 20165303 Exp4 恶意代码分析
			
实践目标 1.1是监控你自己系统的运行状态,看有没有可疑的程序在运行. 1.2是分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用原生指令或sysinternals,systr ...
 - erlang的热更新
			
erlang作为一个为电信级别而出现的语言,热更新是其最重要的特性之一 热代码升级-Erlang允许程序代码在运行系统中被修改.旧代码能被逐步淘汰而后被新代码替换.在此过渡期间,新旧代码是共存的. ...
 - MariaDB主从复制,redis发布订阅,持久化,以及主从同步
			
一. MariaDB主从复制 mysql基本操作 1 连接数据库 mysql -u root -p -h 127.0.0.1 mysql -u root -p -h 192.168.12.60 2 ...
 - Linux中一个快速查找文件和目录的命令
			
功能介绍: locate命令其实是find -name的另一种写法,但是要比后者快得多,原因在于它不搜索具体目录,而是搜索一个数据库/var/lib/locatedb,值得注意的是:版本不同,会有所不 ...
 - js 实用小技巧
			
https://blog.csdn.net/www93111/article/details/76176771
 - Scratch安装使用教程
			
一.说明 一直听说scratch是一款麻省理工所开发的很好的少儿编程学习工具,一直不是很清楚所谓少儿编程是长什么样所以探究了一下. 二.安装 scratch当前到了3.0版本,3.0版本默认直接是we ...
 - Python3列表(list)比较操作教程
			
一.相等比较 1.1 同顺序列表比较 顺序相同直接用“==”进行比较即可 list1 = ["one","two","three"] lis ...