Java提高——多线程(五)生产消费者问题

生产者/消费者问题是个典型的多线程问题,类似于hello world对于一门编程语言而言,涉及的对象包括“生产者”、“消费者”、“仓库”和“产品”。

该模型需要注意以下几点:

1、生产者只有在仓库未满的时候生产,仓满则停止生产。

2、消费者只有在仓库有产品的情况下才能消费,空仓则等待。

3、当消费者发现没有产品时通知生产者生产。

4、生产者在生产出可消费的产品时,通知等待的消费者去消费。

此模型需要结合Object类的wait、notify、notifyAll方法。

package Progress.Thread;

import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;

/**
 * @date 2018/5/4 16:07
 * 消费者/生产者问题
 */
public class Test {
    public static void main(String[] args) {
        Godown godown = new Godown(100);
        Producer pro = new Producer(godown);
        Consumer con = new Consumer(godown);

        pro.produce(60);
        pro.produce(120);
        con.consume(90);
        con.consume(150);
        con.consume(110);
    }
}
/**仓库*/
class Godown extends Thread{
    /**仓库容量*/
    private int capacity;
    /**仓库的实际容量*/
    private int size;

    public Godown() {
    }

    public Godown(int capacity) {
        this.capacity = capacity;
        this.size = 0;
    }

    public synchronized void produce(int value){
        //value表示想要生产的数量(有可能生产量太多需要多次生产)
        try {
            while (value>0){
                //库存满的时候等待消费者消费产品
                while (size>=capacity) {
                    wait();
                }
                    //获取实际生产的数量(即库存中增加的数量)
                    //如果 库存+想要生产的数量>总的容量,则 实际增量=总的容量-实际容量(此时仓库满仓)
                    //否则 实际增量=想要生产的数量
                    int increment = (size+value)>capacity ? capacity - size : value;
                    size = size + value;
                    value -= increment;
                    System.out.println(Thread.currentThread()+" "+getName()+" "+value+" "+size+" "+increment);
                    //System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n",
                    //Thread.currentThread().getName(), increment, size,value);

                    //通知消费者消费
                    notifyAll();

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void consume(int value){
        try {
            //value表示要消费的数量(可能消费量太大数量不够,要多次提供以供消费)
            while (value>0){
                //库存为0时,等待生产者生产
                while (size<=0) {
                    wait();
                }
                    //获取实际消费的数量,即库中实际减少的量
                    //如果 库存<要消耗的量,则 实际消耗量 = 库存
                    //否则,实际消耗的量 = 要消耗的量
                    int decent = size<value?size:value;
                    size -= decent;
                    value -= decent;
                    System.out.println(Thread.currentThread()+" "+getName()+" "+value+" "+size+" "+decent);
                    //System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n",
                    //Thread.currentThread().getName(), decent,value, size);
                    //通知生产者生产
                    notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return "Godown{" +
                "capacity=" + capacity +
                ", size=" + size +
                '}';
    }
}
/**生产者*/
class Producer extends Thread{
    private Godown godown;

    public Producer(Godown godown) {
        this.godown = godown;
    }

    //新建一个线程向仓库中生产产品
    public void produce(final int val){
        new Thread(){
            @Override
            public void run(){
                godown.produce(val);
            }
        }.start();
    }
}

/**消费者*/
class Consumer extends Thread{
    private Godown godown;

    public Consumer(Godown godown) {
        this.godown = godown;
    }

    //新建一个线程去消耗产品
    public void consume(final int val){
        new Thread(){
            @Override
            public void run(){
                godown.consume(val);
            }
        }.start();
    }
}

生产者与仓库关联,当调用produce方法时,会生成一个线程向仓库中生产产品。

消费者与仓库关联,当调用consume方法时,会生成一个线程从仓库中获得商品用于消费。

produce方法和consume方法都是synchronized的意味着进入就会获得仓库对象的同步锁。也就是说同一时间,生产者和消费者线程只能有一个能够运行,另一个必须等待,通过同步锁实现了对“残酷”的互斥访问。

produce方法:当仓库满时,生产者线程等待,等待消费者消费商品之后,生产者才生产;生产之后通过notifyAll唤醒同步锁上的所有线程,包括消费者线程,“通知消费者消费”

consume方法:当空仓的时候,消费者等待,等待生产者生产商品之后,消费者才消费;消费者通过notifyAll唤醒同步锁上的所有线程,包括生产者线程,“通知生产者生产”

转载请注明出处:http://www.cnblogs.com/skywang12345/p/3480016.html

posted @ 2018-05-07 16:36  惶者  阅读(141)  评论(0编辑  收藏  举报