完全理解Java中生产者和消费者模型

合集下载

生产者与消费者问题(Java)

生产者与消费者问题(Java)

package Table;public class Message {public static int id;public String content;public String getContent() {return content;}public void setContent(String content) {this.content = content;}public int getId() {return id;}public void setId(int id) {Message.id = id;}}package Table;import java.util.ArrayList;import java.util.List;public class Queue {List<Message> queue = new ArrayList<Message>();/** 队列中message对象的最大值,默认为10 */int maxMessageNum = 10;public synchronized void produce(Message message) {/**synchronized为关键字,表示在任何一个线程要访问缓冲区时都会检查有无其他线程访问此段内容,有的话则等待,无的话则直接访问**/this.notifyAll();while (queue.size() == maxMessageNum) {System.out.println(Thread.currentThread().getName()+ "the desk is full, and the chef want to relax");try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}queue.add(message);System.out.println(Thread.currentThread().getName() + "the chef make a deal"+ message.getContent() + ":" + getCount());}public synchronized void consume() {this.notifyAll();while (queue.size() == 0) {System.out.println(Thread.currentThread().getName()+ "the desk is empty, the consumer is complaining");try {wait();} catch (InterruptedException e) {e.printStackTrace();}}Message message = queue.get(0);queue.remove(0);System.out.println(Thread.currentThread().getName() + "the consumer takes away one deal"+ message.getContent() + ": " + getCount());}public synchronized int getCount() {return queue.size();}}package Table;public class Run {public static void main(String args[]) {Queue Q = new Queue();Producer wQ1 = new Producer(Q);Producer wQ2 = new Producer(Q);Consumer rQ1 = new Consumer(Q);Consumer rQ2 = new Consumer(Q);Consumer rQ3 = new Consumer(Q);Thread threadWQ1 = new Thread(wQ1, "thread-wQ1"); Thread threadWQ2 = new Thread(wQ2, "thread-wQ2");Thread threadRQ1 = new Thread(rQ1, "thread-rQ1"); Thread threadRQ2 = new Thread(rQ2, "thread-rQ2"); Thread threadRQ3 = new Thread(rQ3, "thread-rQ3");threadWQ1.start();threadWQ2.start();threadRQ1.start();threadRQ2.start();threadRQ3.start();}}class Producer extends Thread {private Queue queue;Producer(Queue queue) {this.queue = queue;}public void run() {while (true) {Message message = new Message();message.setId(++Message.id);message.setContent("food"+Message.id);queue.produce(message);try {sleep(1000);} catch (Exception e) {}}}}class Consumer extends Thread { private Queue queue;Consumer(Queue queue) {this.queue = queue;}public void run() {while (true) {queue.consume();try {sleep(1000);} catch (Exception e) {}}}}。

Java学习教程031405_综合案例:生产者与消费者

Java学习教程031405_综合案例:生产者与消费者

Object类对多线程的支持
No.
方法
1
public final void wait() throws InterruptedException
2
public final void wait​(long timeout) throws InterruptedException
3
public final void wait​(long timeout, int nanos) throws InterruptedException
第14章:多线程编程
综合案例:生产者与消费者
生产者与消费者
➢ 在多线程操作中有一个经典的案例程序 —— 生产者和消费者 问题,生产者不断生产,消费者不断取走生产者生产的产品
解决数据同步问题
➢ 数据同步的问题只能够通过同步代码块或同步方法完成,在本 程序中,生产者和消费者代表着不同的线程对象,所以此时的 同步操作应该设置在Message类之中,可以将title与content 属性设置定义为单独同步方法。
4
public final void notify()
5
public final void notifyAll()
类型 普通
普通
线程的等待
描述
设置线程等待毫秒数
普通
普通 普通
设置线程等待毫秒数和纳秒数
唤醒第一个等待线程 唤醒全部等待线程
生产者消费者等待操作

生产者消费者实验报告

生产者消费者实验报告

生产者消费者实验报告生产者消费者实验报告引言:生产者消费者模型是计算机科学中的一个经典问题,用于解决多线程并发访问共享资源的同步问题。

在本实验中,我们通过编写一个简单的Java程序来模拟生产者消费者模型,并观察其运行结果和效果。

一、实验背景生产者消费者模型是一种常见的并发模型,用于解决多线程访问共享资源时可能出现的数据竞争和同步问题。

在该模型中,生产者负责生产数据并将其放入共享缓冲区,而消费者则负责从缓冲区中取出数据进行消费。

为了确保生产者和消费者之间的同步与互斥,需要使用合适的同步机制,如信号量、互斥锁等。

二、实验目的本实验的主要目的是通过编写一个简单的生产者消费者程序,验证该模型在多线程环境下的正确性和效果。

我们将通过观察程序的输出结果和运行时间来评估其性能,并分析其中可能存在的问题和改进空间。

三、实验设计1. 编写生产者类和消费者类:我们首先定义了一个共享缓冲区,用于存储生产者生产的数据。

然后,我们编写了一个生产者类和一个消费者类,分别实现了生产者和消费者的逻辑。

在生产者类中,我们使用了一个循环来模拟生产者不断地生产数据,并将其放入缓冲区。

而在消费者类中,我们同样使用了一个循环来模拟消费者不断地从缓冲区中取出数据进行消费。

2. 同步机制的选择:为了保证生产者和消费者之间的同步与互斥,我们选择了信号量作为同步机制。

在生产者类中,我们使用一个信号量来控制缓冲区的可用空间,当缓冲区已满时,生产者将等待,直到有可用空间。

而在消费者类中,我们同样使用一个信号量来控制缓冲区的可用数据,当缓冲区为空时,消费者将等待,直到有可用数据。

3. 实验参数的设置:为了模拟真实的生产者消费者场景,我们设置了以下参数:- 缓冲区大小:10- 生产者数量:3- 每个生产者生产的数据量:1000- 消费者数量:2四、实验结果与分析在运行实验程序后,我们观察到以下结果:1. 生产者和消费者之间的同步与互斥得到了有效保证,生产者在缓冲区已满时会等待,直到有可用空间;消费者在缓冲区为空时会等待,直到有可用数据。

生产者与消费者问题

生产者与消费者问题

⽣产者与消费者问题⽣产者与消费者问题是Java多线程中⼀道⾮常经典的问题,问题如下: ⽣产者与消费者问题也称缓存问题,⽣产者与消费者即Java 中的线程,⽣产者与消费者问题即⽣产者⽣产⼀定数量的线程放⼊缓存区中,供消费者消费者消费,在消费和⽣产的过程中,如果⽣产者⽣产的产品超过了缓存区的上限则停⽌⽣产,等待消费者消费,如果缓存区的产品被消费完,消费者则停⽌消费,等待⽣产者⽣产 ⾸先,我们来看题⽬,从题⽬中我们⼀个可以抽取出⼏个实体类呢?答案是4个 Consumer(消费者),Producer(⽣产者),Product(产品),WareHouse(缓冲区,也叫仓库),于是项⽬结构如下,main 为测试类产品类package ProducersAndConsumers;//产品public class Product {//产品需要⼀个id 来表明产品的唯⼀性private Integer productId;//id直接由构造⽅法传⼊public Product(Integer productId) {this.productId = productId;}public Integer getProductId() {return productId;}@Overridepublic String toString() {return "Product{" +"productId=" + productId +'}';}}仓库package ProducersAndConsumers;import java.util.LinkedList;//仓库类public class WareHouse {//仓库容量,我们设置为10个private final int max = 10;//仓库基础的数量private final int base = 0;//我们设置⼀个集合来存放⽣产的产品,由于我们需要⼀个可以弹出最后⼀个产品的⽅法,所以我们在这⾥使⽤LinkedListprivate LinkedList<Product> products = new LinkedList<>();//⽣产⽅法public synchronized void push(Product product) {//判断是否有空间存放产品while(max==products.size()){try{System.out.println("仓库已满,消费者快来消费"+Thread.currentThread().getName()+"停⽌⽣产");//仓库满后停⽌当前线程this.wait();}catch (Exception ex){ex.printStackTrace();}}//⽣产商品products.addLast(product);System.out.println(Thread.currentThread().getName()+"⽣产了⼀个产品:"+product.getProductId()+"号");try{//等待1秒,⽅⾯我们观察Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}notifyAll();}//消费⽅法public synchronized void pop() {//判断是否有产品while (products.size()==base){try{System.out.println("仓库空了,⽣产者快点⽣产"+Thread.currentThread().getName()+"停⽌消费");//仓库空后停⽌当前线程this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//消费商品System.out.println(Thread.currentThread().getName()+"消费了⼀个产品:"+products.getLast().getProductId()+"号"); products.removeLast();try{//等待1秒,⽅⾯我们观察Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}notifyAll();}}⽣产者package ProducersAndConsumers;//⽣产者public class Producer implements Runnable {//⽣产产品的idprivate int count = 0;//仓库private WareHouse wareHouse;//⽣产者和消费者都是⽤同⼀个仓库,所以我们只要声明⼀个仓库,在由构造⽅法传⼊即可public Producer(WareHouse wareHouse) {this.wareHouse = wareHouse;}//⽣产⽅法@Overridepublic void run() {while (true){Product product = new Product(count);wareHouse.push(product);// 产品id不可重复,所以我们使⽤⾃增策略count++;}}}消费者package ProducersAndConsumers;public class Consumer implements Runnable{//仓库private WareHouse wareHouse;//⽣产者和消费者都是⽤同⼀个仓库,所以我们只要声明⼀个仓库,在由构造⽅法传⼊即可public Consumer(WareHouse wareHouse) {this.wareHouse = wareHouse;}//消费⽅法@Overridepublic void run() {while (true){wareHouse.pop();}}}最后测试类package ProducersAndConsumers;//测试类public class Main {public static void main(String[] args) {WareHouse wareHouse = new WareHouse();Producer producer = new Producer(wareHouse);Consumer consumer = new Consumer(wareHouse); Thread producerT = new Thread(producer,"⽣产者"); Thread consumerT = new Thread(consumer,"消费者"); producerT.start();consumerT.start();}}。

Java实现Kafka的生产者和消费者例子

Java实现Kafka的生产者和消费者例子

Java实现Kafka的⽣产者和消费者例⼦Kafka的结构与RabbitMQ类似,消息⽣产者向Kafka服务器发送消息,Kafka接收消息后,再投递给消费者。

⽣产者的消费会被发送到Topic中,Topic中保存着各类数据,每⼀条数据都使⽤键、值进⾏保存。

每⼀个Topic中都包含⼀个或多个物理分区(Partition),分区维护着消息的内容和索引,它们有可能被保存在不同服务器。

新建⼀个Maven项⽬,pom.xml 加⼊依赖:<dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>2.3.0</version></dependency>1、编写⽣产者将消息投递到Kafka服务器的名称为“topic1”的Topic中package com.example.kafkatest;import org.apache.kafka.clients.producer.KafkaProducer;import org.apache.kafka.clients.producer.ProducerRecord;import mon.serialization.StringSerializer;import java.util.Properties;public class Producer {public static void main(String[] args) {//配置信息Properties props = new Properties();//kafka服务器地址props.put("bootstrap.servers", "localhost:9092");//设置数据key和value的序列化处理类props.put("key.serializer", StringSerializer.class);props.put("value.serializer", StringSerializer.class);//创建⽣产者实例KafkaProducer<String,String> producer = new KafkaProducer<>(props);ProducerRecord record = new ProducerRecord<String, String>("topic1", "userName", "lc");//发送记录producer.send(record);producer.close();}}运⾏后,可打开命令⾏⼯具,进⼊Kafka⽬录,执⾏命令查询服务器的Topic:bin\windows\kafka-topics.bat --list --zookeeper localhost:2181结果如下:2、编写消费者本例中,消费者和⽣产者在同⼀个项⽬中,只是使⽤不同的启动类。

线程安全生产者消费者

线程安全生产者消费者

线程安全生产者消费者线程安全生产者消费者模型是一种常见的多线程并发模型,用于解决多线程环境下共享资源的安全访问问题。

生产者负责生产共享资源,消费者负责消费共享资源,通过同步机制保证生产者和消费者的安全操作。

在该模型中,生产者和消费者共享一个有限的缓冲区,生产者将数据放入缓冲区,消费者从缓冲区中取出数据。

为了确保线程安全,需要保证以下三个关键点:1. 互斥访问:同一时间只能有一个线程访问共享资源。

通过使用互斥锁或信号量等同步机制,可以确保在同一时间只有一个线程在访问共享资源。

当一个线程进入临界区时,需要对共享资源进行加锁,其他线程在锁被释放之前等待。

2. 空闲检测:当缓冲区为空时,消费者无法继续消费,需要等待生产者生产数据。

同样地,当缓冲区已满时,生产者无法继续生产,需要等待消费者消费数据。

通过使用条件变量或信号量等同步机制,可以在缓冲区为空或已满时,唤醒等待的线程。

3. 缓冲区管理:缓冲区负责存储生产者生产的数据,并提供接口供消费者取出数据。

缓冲区的大小需要合理设定,既要保证生产者和消费者的工作效率,又要避免数据丢失或溢出的情况。

缓冲区的读写操作需要保证线程安全,通过使用互斥锁或读写锁等同步机制,可以确保在同一时间只有一个线程对缓冲区进行读写。

在实现线程安全生产者消费者模型时,需要注意的是线程间的通信和并发控制。

合理地选择同步机制,通过互斥锁、条件变量和信号量等机制,可以确保线程安全的生产者消费者模型的正常运行。

总之,线程安全生产者消费者模型是一种重要的并发模型,用于解决多线程环境下共享资源的安全访问问题。

通过合理地选择同步机制和管理缓冲区,可以保证生产者和消费者的安全操作,提高系统的并发性和效率。

在实际应用中,我们需要根据具体需求和场景,选择合适的同步机制和优化策略,以实现线程安全的生产者消费者模型。

java多线程模拟生产者消费者问题

//java多线程模拟生产者消费者问题//ProducerConsumer是主类,Producer生产者,Consumer消费者,Product产品//Storage仓库public class ProducerConsumer {public static void main(String[] args) {Storage s = new Storage();Producer p = new Producer(s);Consumer c = new Consumer(s);Thread tp = new Thread(p);Thread tc = new Thread(c);tp.start();tc.start();}}class Consumer implements Runnable {//消费者Storage s = null;public Consumer(Storage s){this.s = s;}public void run() {for(int i=0; i<20; i++){Product p = s.pop();//取出产品try {Thread.sleep((int)(Math.random()*1500));} catch (InterruptedException e) {e.printStackTrace();}}}}class Producer implements Runnable {//生产者Storage s = null;public Producer(Storage s){this.s = s;}public void run() {for(int i=0; i<20; i++){Product p = new Product(i);s.push(p); //放入产品// System.out.println("生产者放入:" + p);try {Thread.sleep((int)(Math.random()*1500));} catch (InterruptedException e) {e.printStackTrace();}}}}class Product {int id;public Product(int id){this.id = id;}public String toString(){//重写toString方法return"产品:"+this.id;}}class Storage {int index = 0;Product[] products = new Product[5];public synchronized void push(Product p){//放入while(index==this.products.length){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}this.products[index] = p;System.out.println("生产者放入"+index+"位置:" + p);index++;this.notifyAll();}public synchronized Product pop(){//取出while(this.index==0){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}index--;this.notifyAll();System.out.println("消费者从"+ index+ "位置取出:"+ this.products[index]);return this.products[index];}}。

java网络编程生产者消费者问题

网络编程实验报告实验题目:生产者/消费者问题*名:**学号:**********班级:信息安全091班完成时间:2011.10.25—2011.11.05 ****: ***一、实验概述1.1 实验目的了解进程的同步、互斥机制,认识理解其过程,并用于解决生产者/消费者问题。

使用java编程实现“生产者——消费者”多线程同步问题。

1.2任务描述有两个生产者,每个生产者每次可以生产1个产品,有一个消费者每次消费2个产品,仓库最多只能存放2个产品,若仓库满,则生产者需等待空位来存放产品,若仓库产品数量不足,则消费者需等待来消费产品。

请用多线程技术实现以上功能,要求生产者存放产品时按先来先服务的队列原则来存放产品。

二、功能及处理逻辑设计2.1具体要求1.三个线程。

t0,t1分别代表生产者A,生产者B。

t2代表消费者consumer2.一个仓库(缓冲区),最大存量为两个产品2.1 功能及模块设计2.2特别功能1. Thread.sleep((int)(Math.random()方法令线程随机发生。

2. 用synchronized来实现同步,当仓库满时,等待消费线程,若发生生产者线程则立即等待,进入沉睡状态,当消费线程发生时,同时逐一激活已沉睡生产进程,实现先来先服务。

3. 用synchronized来实现同步,当仓库未满时,发生消费线程,则等待(睡眠),当发生线程,使仓库数为2,激活消费线程。

4. 定义一个新线程必须重写父类的run方法。

2.3算法搭建三、源代码及方法解释(重点步骤已用红色注释)本次方法中未用到队列,利用的是多次调用线程,逐一实现先来先服务import java.util.*;public class xiancheng{public static void main(String[] args){StoreHouse storeHouse = new StoreHouse();Producer producer1 = new Producer("生产者A", storeHouse);Producer producer2 = new Producer("生产者B", storeHouse);Consumer comsumer = new Consumer("消费者", storeHouse);Thread t0 = new Thread(producer1);Thread t1 = new Thread(producer2);Thread t2 = new Thread(comsumer);t0.start();t1.start();t2.start();}}class Producer extends Thread implements Runnable //定义生产者线程{private String producerName = null;private StoreHouse storeHouse = null;public Producer(String producerName, StoreHouse storeHouse) {this.producerName = producerName;this.storeHouse = storeHouse;} //声明生产者要放产品的仓库public void setProducerName(String producerName){this.producerName = producerName;}public String getProducerName(){return producerName;}public void run()//定义一个新线程必须重写父类的run方法{while (true){storeHouse.store_in(this);try{Thread.sleep((int)(Math.random() * 2500));}//定义线程休眠时间catch (InterruptedException e){return;}//有可能抛出异常,必须对它进行捕捉}}}class Consumer extends Thread implements Runnable{//定义消费者线程public String consumerName = null;public StoreHouse storeHouse = null;public Consumer(String consumerName, StoreHouse storeHouse){this.consumerName = consumerName;this.storeHouse = storeHouse;}public void setConsumerName(String consumerName){this.consumerName = consumerName;}public String getConsumerName(){return consumerName;}public void run(){while (true){storeHouse.store_out(this);try{Thread.sleep((int)(Math.random() * 5000)); } //定义线程休眠时间catch (InterruptedException e){return;}}}}class StoreHouse{int count = 0;//创建仓库,用来放产品public synchronized void store_in(Producer pro){while (count == 2) //仓库容量为2{System.out.println("仓库已满," + pro.getProducerName() + "正等待生产...");try{this.wait();//生产者线程进入沉睡}catch (InterruptedException e){}}count++;//产品数自加1System.out.println(pro.getProducerName() + " 生产了 1个产品,库存为" + count); //激活等待的线程,实现先来先服务notify();}public synchronized void store_out(Consumer con){while (count == 0 || count == 1){try{System.out.println("仓库数量不足,消费者等待消费...");this.wait();}catch (InterruptedException e){}}count = count-2;System.out.println(con.getConsumerName() + " 消费了 2个产品,库存为" + count);notify();}}四、程序截图1.先来先服务效果2.消费者等待情况消费者等待的前提下,当仓库数为时立即激活线程,消费当消费者等待时,若仓库每增加一个商品,总会检验是否为满,为则激活消费3.程序效果图总览:其中体现了线程随机性发生,等等各方面情况五、实验总结本次试验我基本上掌握了java多线程同步的方法,完成老师的任务,但是熟练程度还远远未够,将来我也必须更加努力,力求让自己java 编程能力更强。

生产者消费者模式(吃包子例子)

生产者消费者模式(吃包子例子)生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。

在同一个进程地址空间内执行的两个线程生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。

消费者线程从缓冲区中获得物品,然后释放缓冲区。

当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。

当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。

生产者消费者模式是并发、多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式,生产者和消费者可以以不同的速度生产和消费数据。

这篇文章我们来看看什么是生产者消费者模式,这个问题也是多线程面试题中经常被提及的,使用生产者消费者模式的好处。

真实世界中的生产者消费者模式。

生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。

比如一个人正在准备食物(生产者),而另一个人正在吃(消费者),他们使用一个共用的桌子用于放置盘子和取走盘子,生产者准备食物,如果桌子上已经满了就等待,消费者(那个吃的)等待如果桌子空了的话。

这里桌子就是一个共享的对象。

在 Java Executor框架自身实现了生产者消费者模式它们分别负责添加和执行任务。

它的确是一种实用的设计模式,常用于编写多线程或并发代码。

下面是它的一些优点:1它简化的开发,你可以独立地或并发的编写消费者和生产者,它仅仅只需知道共享对象是谁2生产者不需要知道谁是消费者或者有多少消费者,对消费者来说也是一样3生产者和消费者可以以不同的速度执行4分离的消费者和生产者在功能上能写出更简洁、可读、易维护的代码生产者消费者问题是一个流行的面试题,面试官会要求你实现生产者消费者设计模式,以至于能让生产者应等待如果队列或篮子满了的话,消费者等待如果队列或者篮子是空的。

这个问题可以用不同的方式来现实,经典的方法是使用wait和notify方法在生产者和消费者线程中合作,在队列满了或者队列是空的条件下阻塞,Java5的阻塞队列(BlockingQueue)数据结构更简单,因为它隐含的提供了这些控制,虽然现在你不需要使用wait和nofity在生产者和消费者之间通信了,阻塞队列的put()方法将阻塞如果队列满了,队列take()方法将阻塞如果队列是空的。

生产者消费者 线程安全

生产者消费者线程安全生产者消费者模型是一个经典的多线程同步问题,它描述了生产者和消费者在共享资源中的协作关系。

生产者线程负责生产数据,消费者线程负责消费数据,它们通过共享的缓冲区进行通信。

在实现生产者消费者模型时,需要关注线程安全性,保证生产者和消费者之间的操作在多线程环境下能够正确执行。

以下是一个实现线程安全的生产者消费者模型的伪代码:```javaclass Buffer {private int bufferSize;private Queue<Integer> queue;public Buffer(int size) {bufferSize = size;queue = new LinkedList<>();}public synchronized void produce(int item) {while (queue.size() == bufferSize) {try {wait(); // 缓冲区满,等待消费者消费} catch (InterruptedException e) {e.printStackTrace();}}queue.add(item);notifyAll(); // 通知消费者可以消费}public synchronized int consume() {while (queue.size() == 0) {try {wait(); // 缓冲区空,等待生产者生产 } catch (InterruptedException e) {e.printStackTrace();}}int item = queue.remove();notifyAll(); // 通知生产者可以生产return item;}}class Producer implements Runnable {private Buffer buffer;public Producer(Buffer b) {buffer = b;}public void run() {for (int i = 0; i < 10; i++) {buffer.produce(i); // 生产数据}}}class Consumer implements Runnable {private Buffer buffer;public Consumer(Buffer b) {buffer = b;}public void run() {for (int i = 0; i < 10; i++) {int item = buffer.consume(); // 消费数据System.out.println("Consumer consumed: " + item);}}}```在上述代码中,定义了一个`Buffer`类作为共享缓冲区,它包含了一个整型的`queue`来存储数据。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

生产者与消费者模型
在平时的编程中,经常遇到一个线程要产生数据,而另一个线程要处理产生
出来的数据,这其实就是生产者和消费者的关系。生产者在产生数据后可以直接
调用消费者处理数据;也可以把数据放在一个缓冲区中,让消费者从缓冲区中取
出数据处理,两种方式从调用方式上来说,第一种可是说是同步的,即生产者在
生产出数据后要等待消费者消耗掉后才能生产下一个数据,等待时间的长短取决
于消费者处理数据的能力;第二种方式是异步的,生产者只管生产数据,然后扔
到一个缓冲区内,不管数据是否被立即处理了,消费者则从缓冲区中依次取出数
据进行自己节奏的处理。从线程模型角度来说,第一种是单线程的,而第二种则
是多线程的。多线程必须要考虑的一个问题是线程之间的协作,协作即协调合作,
不要乱套,以生产者和消费者模型而言,就是当缓冲区里没有数据时消费者要等
待,等待生产者生产数据,当缓冲区满的时候生产者要等待,等待消费者消耗掉
一些数据空出位置好存放数据。
java中为了实现多线程之间的协助,需要用到几个特性:wait(),notify
(),notifyAll(),synchronized,synchronized相当于操作系统里的临界区
或者锁的概念,所谓临界区就是说一次只能有一个线程进去,其他想进入的线程
必须等待,加了synchronized锁后,才能调用wait(),notify()和notifyAll
()操作,wait方法被调用后,当前线程A(举例)进入被加锁对象的线程休息
室,然后释放锁,等待被唤醒。释放的锁谁来获取?当然是由先前等待的另一个
线程B得到,B在获得锁后,进行某种操作后通过notify或者notifyAll把A
从线程休息室唤醒,然后释放锁,A被唤醒后,重新获取锁定,进行下一语句的
执行。
再回到生产者和消费者模型,如果引入了缓冲区的话就需要处理生产者线程
和消费者线程之间的协作,缓冲区可以有这几种,队列缓冲区,比如队列或者栈,
队列缓冲区的特点是其长度是动态增长的,这就意味着内存的动态分配带来的性
能开销,同时队列缓冲区还会产生因为多线程之间的同步和互斥带来的开销。环
形缓冲区可以解决内存分配带来开销的问题,因为环形缓冲区长度是固定的。但
是环形缓冲区还是无法解决同步互斥带来的多线程切换的开销,如果生产者和消
费者都不止一个线程,带来的开销更大,终极解决办法是引入双缓冲区,何为双
缓冲区?双缓冲区顾名思义是有两个长度固定的缓冲区A B,生产者和消费者只
使用其中一个,当两个缓冲区都操作完成后完成一次切换,开始时生产者开始向
A里写数据,消费者从B里读取数据,当A写满同时B也读完后,切换一下,这
时消费者从A里取数据,生产者向B写数据,由于生产者和消费者不会同时操作
同一个缓冲区,所以不会发生冲突。
生产者和消费者模型不止是用在多线程之间,不同进程之间也可以有。线程
和进程到底有什么区别?这是很多程序员搞不清的问题,其实很简单,进程有自
己的地址空间和上下文,线程是在一个进程上并发执行的代码段。其实在win32
系统中进程只是占用一定长度的地址空间,进程中总是有一个主线程来运行。消
费者和生产者模型应用于进程间通信的典型例子是分布式消息处理,消息的消费
者进程需要一个缓冲区缓冲收到的消息,消息的生产者进程也需要一个缓冲区缓
冲将要发送的消息,这样可以一定程度上减少因为网络断开引起的消息丢失。
对于此模型,应该明确一下几点:
1,生产者仅仅在仓储未满时生产,仓满则停止生产。
2, 消费 者仅仅在仓储有产品时才能消费,仓空则等待。
3, 当消费者发现仓储没有产品的时候会通知生产者生产。
4, 生产者在生产出可消费产品的时候,应该通知等待的消费者去消费。
以下是它的具体实现:
1, public class ProducerConsumer {
2, public static void main(String []args) {
3, SyncStack ss=new SyncStack();
4, Producer p=new Producer(ss);
5, Consumer c=new Consumer(ss);
6, new Thread(p).start();
7, new Thread(c).start();
8, }
9, }
10,
11, class WoTou {
12, int id;
13, WoTou(int id) {
14, this.id=id;
15, }
16, public String toString() {
17, return "WoTou : "+id;
18, }
19, }
20,
21, class SyncStack {
22, int index=0;
23, WoTou[] arrWT=new WoTou[6];
24,
25, public synchronized void push(WoTou wt) {
26, while(index==arrWT.length) {
27, try{
28, this.wait();
29, }catch(InterruptedException e) {
30, e.printStackTrace();
31, }
32, }
33, this.notify();
34, arrWT[index]=wt;
35, index++;
36, }
37,
38, public synchronized WoTou pop() {
39, while(index==0) {
40, try{
41, this.wait();
42, }catch(InterruptedException e) {
43, e.printStackTrace();
44, }
45, }
46, this.notify();
47, index--;
48, return arrWT[index];
49, }
50, }
51,
52, class Producer implements Runnable {
53, SyncStack ss=null;
54, Producer(SyncStack ss) {
55, this.ss=ss;
56, }
57,
58, public void run() {
59, for(int i=0;i<20;i++) {
60, WoTou wt=new WoTou(i);
61, ss.push(wt);
62, System.out.println("生产了:"+wt);
63, try{
64, Thread.sleep((int)(Math.random()*2));
65, }catch(InterruptedException e) {
66, e.printStackTrace();
67, }
68, }
69, }
70, }
71,
72, class Consumer implements Runnable {
73, SyncStack ss=null;
74, Consumer(SyncStack ss) {
75, this.ss=ss;
76, }
77,
78, public void run() {
79, for(int i=0;i<20;i++) {
80, WoTou wt=ss.pop();
81, System.out.println("消费了:"+wt);
82, try{
83, Thread.sleep((int)(Math.random()*1000));
84, }catch(InterruptedException e) {
85, e.printStackTrace();
86, }
87, }
88, }
89, }
附:1程序、进程和线程
程序,就是一段静态的可执行的代码。
进程,就是程序的一次动态的执行过程。
线程,是程序 从头到尾的执行路线,也称为轻量级的进程。一个进程在执行过程
中,可以产生多个线程,形成多个执行路线。但线程间是彼此相互独立的。各个线程可
以共享相同的内存空间,并利用共享内存来完成数据交换、实时通信和一些同步的工作。
而进程都占有不同的内存空间。
单线程是指一个 程序只有一条从开始到结束的顺序的执行路线。
多线程是多个彼此独立的线程,多条执行路线。
2.wait()、notify()可以在任何位置调用,suspend()、resume()只能在synchronized()方法或代码
块中调用。
3线程同步
当多个用户线程在并发运行中,可能会因为同时访问一些内容而产生错误问题。例如,
同一时刻,一个线程在读取数据,另外一个线程在处理数据,当处理数据的线程没有等
到读取数据的线程读取完毕就去处理数据,必然得到错误的结果。

相关文档
最新文档