生产者消费者java实现
生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。
我们在实现实时显示日志的时候,想到了用这个方法:当远程服务器执行命令时,先把日志写入一个缓存中,然后客户端一直读取缓存中的数据,这样就能实现实时读取。当然我们先验证一下生产者消费者多线程同步问题。
一, 定义一个缓存,并定义相应的读写互斥操作。代码如下所示:
//文件Buffer.java
package thread;
import java.sql.Date;
import java.text.SimpleDateFormat;
// 生产者与消费者共享的缓冲区,必须实现读、写的同步
public class Buffer {
private String contents;
private boolean available = false;
public synchronized String get() {
while (! available) {
try {
this.wait();
} catch (InterruptedException exc) {}
}
String value = contents;
// 消费者取出内容,改变存取控制available
available = false;
//System.out.println("取出" + contents);
this.notifyAll();
return value;
}
public synchronized void put(String value) {
while (available) {
try {
this.wait();
} catch (InterruptedException exc) {}
}
contents = value;
available = true;
//System.out.println("放入" + contents);
this.notifyAll();
}
}
通过available变量进行互斥控制,当然具体执行过程我们就不详细讲述,这是一个经典的问题,到处都有讲解。
二, 生产者,往缓存中放入数据。
生产者,不断地往缓存中放入数据,放入前先检测缓存中是否有数据,如果有,则等待数据被读出后再放入。示例代码如下:
//producer.java
package thread;
// 生产者线程
public class Producer extends Thread {
private Buffer buffer;
private int number;
private String mes="good";
public Producer(Buffer buffer, int number) {
this.buffer = buffer;
this.number = number;
}
public void setString(String message)
{
this.mes=message;
}
@SuppressWarnings("deprecation")
public void run() {
for (int i = 0;;) {
buffer.put(this.mes+i);
if(i==10)
{
break;
}
System.out.println("生产者#" + number + "生产: " + i++);
try {
Thread.sleep((int) (Math.random() * 100));
} catch (InterruptedException exc) {}
}
}
此段代码里加了一个条件,当生产者生产了10个数据后,但中断,不再生产。
三, 消费者,读出缓存中的数据
消费者,不断地从缓存中读取数据,在读取数据之前先检测缓存中是否有数据,如果没有则等待;如果有则读取。示例代码如下所示:
//consumer.java
package thread;
// 消费者线程
public class Consumer extends Thread {
private Buffer buffer;
private int number;
private boolean stopflag=true;//设置停止标志
public Consumer(Buffer buffer, int number) {
this.buffer = buffer;
this.number = number;
}
public void setstop(boolean flag)
{
this.stopflag=flag;
}
public void run() {
for (;;) {
String v = buffer.get();
System.out.println("消费者#" + number + "消费:" + v);
if(v.contains("10"))
{
System.out.println("Can be stop!!!");
this.stop();
}
}
}
}
当然消费者代码段中也加了限制,当读取的数据中包含“10”,则输入“Can be stop!!!”,并中断线程执行。这样做的目的是,当生产者停止生产数据时,消费者也需要停止读取进程。
四, 生产者消费者调用程序
我们上面写了缓存的读取,生产者线程和消费者线程,下面我们就写段程序调试一下:
import java.sql.Date;
import java.text.SimpleDateFormat;
// 演示生产者∕消费者问题的主程序
public class ProducerConsumerProblem {
public static void main(String[] args) {
Buffer buffer = new Buffer();
Producer pro= new Producer(buffer, 100);
pro.setString("Today is a fine day!");
pro.start();
new Consumer(buffer, 300).start();
}
}
运行这个程序,则会显示出如下运行结果:
当生产和消费都达到10时,则中断进程执行。