java生产者与消费者模式
生产者和消费者模式是我们在学习多线程中很经典的一个模式,它主要分为生产者和消费者,分别是两个线程.
一:生产者和消费者模式简介
二:生产者和消费者模式的实现
一个自助餐里有一个做饭的厨师和来吃饭的人,消费者消费食物,这里就可以把厨师当做生产者,(吃饭的人当做消费者),而食物则有这样的过程,被厨师生产出来,然后被来吃饭的人消费。当食物存在的时候,厨师等待,不再进行生产,来吃饭的人进行消费。当食物为空的时候,厨师开始生产食物,来吃饭的人等待。这中间就存在着一个线程之间协作的过程。
首先是新建两个线程,一个厨师线程,一个服务员线程,双方进行协作:
以下是厨师线程:
package com.heng.subhey.consumer;
import java.util.concurrent.TimeUnit;
public class Chef implements Runnable{ //product
private Restaurant restaurant;
private int count=0;
public Chef(Restaurant restaurant) {
this.restaurant = restaurant;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
synchronized (this) {
while (restaurant.meal != null) {
wait();
}
if (++count==10){
System.out.println("out of food,closing");
restaurant.exec.shutdownNow();
}
System.out.println("Chef product meal"+count);
synchronized (restaurant.consumerPerson){
restaurant.meal= new Meal(count);
restaurant.consumerPerson.notifyAll();
}
TimeUnit.MICROSECONDS.sleep(100);
}
}
}catch (InterruptedException e){
System.out.println("Chef interruped");
}
}
}
以下是消费者线程:
package com.heng.subhey.consumer;
public class ConsumerPerson implements Runnable{
private Restaurant restaurant;
public ConsumerPerson(Restaurant restaurant) {
this.restaurant = restaurant;
}
@Override
public void run() {
try{
while (!Thread.interrupted()) {
synchronized (this) {
while (restaurant.meal == null) {
wait();
}
System.out.println("consumerPerson got:"+restaurant.meal);
}
synchronized (restaurant.chef){
restaurant.meal=null;
restaurant.chef.notifyAll();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("consumerPerson interrupdate");
}
}
}
我们来分析一下厨师线程,主要看它的run方法,里面包含着一个try、catch块,首先它会一直捕获线程的状态,当它不处于interrupted(线程中断,此时无法运转)异常时,往下走。然后取得当前对象的锁,把它上锁,通过一个while循环,取得餐馆里面的meal,判断其是否为null.当餐馆里面的餐还有剩余的时候,此时生产者不需要工作,它处于wait状态,注意wait和Thread.sleep的区别,sleep方法运行的时候线程是不会丢弃锁的,而wait方法会释放锁,所以此时,被锁住的对象运行到wait方法,它已经释放了锁,因此消费者可以获取到锁。
再接着count会进行累加1。再接着锁住服务员线程,此时服务员线程获取锁的主动权,它通过notifyAll方法唤醒所有的在锁上等待线程,注意此处为什么是notifyAll而不是notify,这主要是因为此刻厨师线程并不知道等待的线程究竟是几条,为了线程安全起见,唤醒所有的等待线程。注意的是:notifyAll会唤醒所有线程,但是运行的只是其中一个,关于这个线程的筛选,是完全随机的。
唤醒了消费者线程,它就会运行到消费者线程,然后我们来看一下它的run方法,和之前的厨师线程差不多,依然是锁住当前的消费者线程,不过线程此时的条件变成了meal为null。接着再获取厨师的锁,使其meal为null,再唤醒厨师线程,接着程序就会运转到厨师线程,执行厨师的run方法。
package com.heng.subhey.consumer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Restaurant{
Meal meal;
ExecutorService exec = Executors.newCachedThreadPool();
ConsumerPerson consumerPerson =new ConsumerPerson(this);
Chef chef=new Chef(this);
public Restaurant(){
exec.execute(chef);
exec.execute(consumerPerson);
}
public static void main(String[] args){
new Restaurant();
}
}
可以看到结果,线程有条不紊的运行,生产者每生产一个meal,发出order up信号,然后消费者消费这个meal,再轮到生产者,再到消费者,这就是生产者和消费者的模式的意义,它们之间同步工作,不会出现消费者线程没有meal的时候仍然去消费,生产者在有meal的时候依然去生产,这样就会产生线程安全的问题。