进入正题之前先说点故事。从最开始学java的那里开始:我是从08年下半年开始学Java,在《我的六年程序之路》中提到了一些。当时比较简单,每天看尚学堂的视频(对于初学者而言看视频好一些。),然后写代码。比较清楚的记得马士兵讲到生产者消费者模型的时候还大谈特谈要是掌握了这个工资可以+1000(现在回忆起有点像历史一样,多少有些伤感)。那时候已经过年了,我在家的时候把那段代码敲了很多遍,不过基本上是默写下来的,也没有仔细的想过其中的一些细节,不久后我就忘记了其中的写法。这里又衍生出一个学习方法的问题。我在学习上一直有一个毛病:学习的过程中不会刨根问底,这导致很多问题思考的不透彻,在后面很大程度上影响我整个知识体系的扎实程度,也应了那句话:出来混迟早要还的,在学习技术的过程中就是要一步一个脚印,总有一天会豁然开朗,不知不觉的发现所有知识都串起来了,一件很神奇的事情。在技术学习的过程中还是需要有点钻牛角尖的精神,基本上技术上的大牛都有钻牛角尖的“毛病”。
第二段故事是在2012年,这是正儿八经的使用生产者消费者模型在项目中进行开发,中间也写出了一大堆问题,这个不是写出来的,是慢慢调试出来的。最开始使用wait(),notify(),后来用CountDownLatch。
第三个故事是前几天的事情,这也是我为什么要写这篇文章的原因。一个同事问我代码的问题,他里面就是用到了生产者消费者,当时我整了半天都没整利索,很没面子。后面虽然找出了问题(这个问题我今天也碰到了),就是习惯性的把整个容器(下面代码的Container)锁住,然后发现只要一个线程wait住,所有线程都不动了,因为这个wait住的线程还持有Container的锁没有释放,其他线程也就进不来。我发现自己对于这个模型并没有完全摸透,就在周末的时候好好的把这个模型从头到尾的写了写,当然从中又有一些新的收获。
/**
* 生产者
* @author 百恼 2013-11-7下午12:18:39
*
*/
public class Producer implements Runnable {
//简单的模拟,这里一个生产容器,设置成final类型的话不允许再次赋值
private final Container container;
//生产者线程监听器
private final Object producerMonitor;
//消费者线程监听器
private final Object consumerMonitor;
public Producer(Object producerMonitor,Object consumerMonitor,Container container){
this.producerMonitor = producerMonitor;
this.consumerMonitor = consumerMonitor;
this.container = container;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
while(true){
produce();
}
}
public void produce(){
//这里为了形象,模拟几个制作面包的步骤
step1();
Bread bread = step2();
//如果发现容器已经满了,生产者要停
if(container.isFull()){
//唤醒消费者
synchronized(consumerMonitor){
if(container.isFull()){
consumerMonitor.notify();
}
}
//生产者挂起,两把锁的问题
synchronized(producerMonitor){
try {
if(container.isFull()){
System.out.println("生产者挂起...");
producerMonitor.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else{
//容器中还有容量,把面包放到容器内,这里可能会有丢失
boolean result = container.add(bread);
System.out.println("Producer:"+result);
}
}
public void step1(){}
public Bread step2(){
return new Bread();
}
}
/**
* 消费者
* @author 百恼 2013-11-7下午12:18:50
*
*/
public class Consumer implements Runnable{
//简单的模拟,这里一个生产容器,设置成final类型的话不允许再次赋值
private final Container container;
//生产者线程监听器
private final Object producerMonitor;
//消费者线程监听器
private final Object consumerMonitor;
public Consumer(Object producerMonitor,Object consumerMonitor,Container container){
this.producerMonitor = producerMonitor;
this.consumerMonitor = consumerMonitor;
this.container = container;
}
@Override
public void run() {
while(true){
consume();
}
}
//消费,两把锁的问题
public void consume(){
//如果发现容器已经满了,生产者要停
if(container.isEmpty()){
//唤醒生产者
synchronized(producerMonitor){
if(container.isEmpty()){
producerMonitor.notify();
}
}
//消费者挂起
synchronized(consumerMonitor){
try {
if(container.isEmpty()){
System.out.println("消费者挂起。。。");
consumerMonitor.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else{
//还有面包可以进行消费
Bread bread = container.get();
System.out.println("bread:"+bread);
}
}
}
/**
* 装产品的容器
* @author 百恼 2013-11-9下午02:06:59
*
*/
public class Container {
private final int capacity;
private final List list;
public Container(int capacity){
this.capacity = capacity;
list = new ArrayList(capacity);
}
public List getList(){
return list;
}
/**
* 添加产品
* @param product
*/
public synchronized boolean add(T product){
if(list.size()=capacity){
return true;
}
return false;
}
public synchronized boolean isEmpty(){
return list.isEmpty();
}
public synchronized T get(){
if(list.size()>0){
return list.remove(0);
}
return null;
}
public synchronized int getSize(){
return list.size();
}
public int getCapacity(){
return capacity;
}
}
/**
* TODO Comment of Client
* @author 百恼 2013-11-7下午12:20:08
*
*/
public class Client {
public static void main(String[] args){
Object producerMonitor = new Object();
Object consumerMonitor = new Object();
Container container = new Container(10);
//生产者开动
new Thread(new Producer(producerMonitor,consumerMonitor,container)).start();
new Thread(new Producer(producerMonitor,consumerMonitor,container)).start();
new Thread(new Producer(producerMonitor,consumerMonitor,container)).start();
new Thread(new Producer(producerMonitor,consumerMonitor,container)).start();
//消费者开动
new Thread(new Consumer(producerMonitor,consumerMonitor,container)).start();
new Thread(new Consumer(producerMonitor,consumerMonitor,container)).start();
}
}