生产者/消费者模式其原理是基于wait/notify实现的。
通过代码更能便于理解,废话不多说,上代码。
生成者和消费者一对一模式
生产者
public class Producer {
private String obj;
public Producer(String obj){
super();
this.obj = obj;
}
public void buildProduct(){
try {
synchronized (obj){
// 如果有产品,生产者等待
if(StringUtils.isNotBlank(ProductObj.product)){
Thread.sleep(1000);
System.out.println("生产者进入等待状态。。。。。。");
obj.wait();
}
//生产产品并通知消费者
Thread.sleep(1000);
System.out.println("生产者生产产品");
ProductObj.product = "我有产品";
obj.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
生产者线程
public class ProducerThread extends Thread{
private Producer producer;
public ProducerThread(Producer producer){
super();
this.producer = producer;
}
@Override
public void run(){
while(true){
producer.buildProduct();
}
}
}
消费者
public class Consumer {
private String obj;
public Consumer(String obj){
super();
this.obj = obj;
}
public void consumeProduct(){
try{
synchronized (obj){
// 如果没有产品,消费者等待
if(StringUtils.isBlank(ProductObj.product)){
Thread.sleep(1000);
System.out.println("消费者进入等待状态------");
obj.wait();
}
// 消费产品并通知生产者
Thread.sleep(1000);
System.out.println("消费者消费产品");
ProductObj.product = "";
obj.notify();
}
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
消费者线程
public class ConsumerThread extends Thread{
private Consumer consumer;
public ConsumerThread(Consumer consumer){
super();
this.consumer = consumer;
}
@Override
public void run(){
while(true){
consumer.consumeProduct();
}
}
}
产品容器
public class ProductObj {
public static String product = new String();
}
测试类
public class Test2 {
public static void main(String[] args){
String obj = new String();
Producer producer = new Producer(obj);
Consumer consumer = new Consumer(obj);
ProducerThread producerThread = new ProducerThread(producer);
producerThread.start();
ConsumerThread consumerThread = new ConsumerThread(consumer);
consumerThread.start();
}
}
结果: 不停的在生产和消费产品。
生产者和消费者多对多模式
我们修改测试类,再修改下生产者和消费者类的打印方式,看下多对多的情况
生产者
public class Producer {
private String obj;
public Producer(String obj){
super();
this.obj = obj;
}
public void buildProduct(){
try {
synchronized (obj){
// 如果有产品,生产者等待
if(StringUtils.isNotBlank(ProductObj.product)){
System.out.println(Thread.currentThread().getName() + "======Wait");
obj.wait();
}
//生产产品并通知消费者
// Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "======Runnable");
ProductObj.product = "我有产品";
obj.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
消费者
public class Consumer {
private String obj;
public Consumer(String obj){
super();
this.obj = obj;
}
public void consumeProduct(){
try{
synchronized (obj){
// 如果没有产品,消费者等待
if(StringUtils.isBlank(ProductObj.product)){
System.out.println(Thread.currentThread().getName() + "------Wait");
obj.wait();
}
// 消费产品并通知生产者
// Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "-----Runnable");
ProductObj.product = "";
obj.notify();
}
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
测试类
public class Test2 {
public static void main(String[] args){
String obj = new String();
Producer producer = new Producer(obj);
Consumer consumer = new Consumer(obj);
ProducerThread[] producerThreads = new ProducerThread[2];
ConsumerThread[] consumerThreads = new ConsumerThread[2];
for(int i = 0; i < 5; i++){
producerThreads[i] = new ProducerThread(producer);
producerThreads[i].setName("生产者" + i);
consumerThreads[i] = new ConsumerThread(consumer);
consumerThreads[i].setName("消费者" + i);
producerThreads[i].start();
consumerThreads[i].start();
}
//
// ProducerThread producerThread = new ProducerThread(producer);
// producerThread.start();
// ConsumerThread consumerThread = new ConsumerThread(consumer);
// consumerThread.start();
}
}
这是结果
上面的结果中标红的地方可以看出消费者0被唤醒后运行,消费产品,然后又竞争到锁,进入wait状态,然后消费者1竞争获得锁,但是这个时候是没有产品的,但是也运行runnable了,可以看出,被唤醒的线程是从wait()方法后继续执行的,也看出了代码的不合理性,对消费者和生产者类稍作改动。
// 如果有产品,生产者等待
//if(StringUtils.isNotBlank(ProductObj.product)){
while(StringUtils.isNotBlank(ProductObj.product)){
System.out.println(Thread.currentThread().getName() + "======Wait");
obj.wait();
}
// 如果没有产品,消费者等待
//if(StringUtils.isBlank(ProductObj.product)){
while(StringUtils.isBlank(ProductObj.product)){
System.out.println(Thread.currentThread().getName() + "------Wait");
obj.wait();
}
运行结果
这样就不会出现上面描述的情况了,但是注意新代码红色框的地方,所有的线程都进入了等待状态,这种情况就是有几率发生的现象--假死,有兴趣的可以自己试试。造成假死的原因是因为消费者的notify()方法唤醒的并不一定是生产者,也有可能是消费者,而生产者的notify()唤醒也不一定是消费者,也有可能是生产者
,所以可能经过N次等待唤醒后可能所有线程全部是wait状态,解决假死其实很简单,就是在生产者和消费者中使用notifyAll()方法,notifyAll()方法是用来唤醒所有等待线程的,这样就不会出现假死现象了。
//obj.notify();
obj.notifyAll();
部分结果截屏,结果没有问题,如果消费者和生产者能交叉进行就更好了。下篇继续改进。
下篇内容:多线程之通知/等待交叉进行