在多线程以及并发工具类中,常用的一种思想就是生产者消费者模式,生产者负责生产物品,将物品放到传送带,消费者负责获取传送带的物品,消费物品。现在只考虑最简单的情况,传送带上只允许放一个物品。
1、传送带为空,则允许生产者放置物品,否则不许放(生产者线程wait)。
2、生产者放置完物品后,通知消费者可以拿了(线程通信,notify 或者notifyAll)。
2、传送带不空,则允许消费者拿物品,否则不许拿(消费者线程wait)。
3、消费者拿走物品后,通知生产者可以继续生产(线程通信,notify 或者notifyAll)。
package com.smikevon.concurrent;
import java.util.Random;
/**
* @description: 生产者消费者模式,通过wait和notify方式来实现
* @date : 2014年9月12日 上午11:39:31
*/
public class ProducerConsumer_01 {
public static void main(String[] args) {
Drop drop = new Drop();
new Thread(new Producer(drop)).start();
new Thread(new Consumer(drop)).start();
}
}
/**
* @description: 传送带,只能有一个物品(message)在上面
* @date : 2014年9月12日 下午12:03:08
*/
class Drop{
private String message;
private boolean empty = true;
public synchronized String take() throws InterruptedException{
while(empty){
wait();
}
empty = true;
notifyAll();
return message;
}
public synchronized void put(String message) throws InterruptedException{
while(!empty){
wait();
}
this.message = message;
empty = false;
notifyAll();
}
}
/**
*
* @description: 生产者随机放入字符串
* @date : 2014年9月12日 上午11:53:27
*/
class Producer implements Runnable {
private Drop drop;
public Producer(Drop drop) {
this.drop = drop;
}
public void run() {
String[] messages = {
"我是",
"一名程序员",
"我很骄傲",
"也很自豪",
"爱岗敬业",
"勤劳奉献",
"无怨无悔",
"奉献青春",
"希望通过学习",
"提升",
"自己",
"done",
};
try {
for(int i=0;i<messages.length;i++){
System.out.format("%s: 放入信息-----------%s %n",Thread.currentThread().getName(),messages[i]);
drop.put(messages[i]);
Thread.sleep(new Random(System.currentTimeMillis()).nextInt(5000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* @description: 消费者,有字符串,就取出来
* @date : 2014年9月12日 下午12:02:35
*/
class Consumer implements Runnable{
private Drop drop;
Consumer(Drop drop){
this.drop = drop;
}
public void run() {
try {
String message = "";
System.out.println(drop);
while(!(message = drop.take()).equals("done")){
System.out.format("%s: 取出信息-------------%s %n",Thread.currentThread().getName(),message);
Thread.sleep(new Random(System.currentTimeMillis()).nextInt(5000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在线程wait的代码处都采用了循环测试条件(专业名称叫条件谓词),如下
while(!empty){
wait();
}
是因为在另一个线程notifyAll,唤醒本线程后,无法确认此时就一定满足测试条件。两个线程不会有问题,但是在更多线程的时候就会出问题,因为你无法确认自己就是被生产者线程唤醒的,可能在唤醒之前已经有其他线程改变过状态变量(本类就是Drop里的message),这样就会出现异常,因此需要在被唤醒后立马测试下条件是否已经满足。将一致性保障交给竞态条件(notifyAll就是等待的线程竞争获取对象的锁)是不良的变成习惯。
下面一节(实现二)会介绍ReenrantLock,里面将等待与唤醒条件进行了细分,让你可以赋予条件真实的意义,而不只将意义绑定到锁的持有上(wait是告诉本线程挂起 和 notify就是告知线程可以抢占对象内置锁了)。