Java多线程系列(三)—生产者消费者模型

来源:互联网 发布:js 判断日期大小 编辑:程序博客网 时间:2024/05/22 17:21

Java多线程系列(三)—生产者消费者模型

生产者消费者模型是一种基于等待/通知模式的经典多线程模型;

个人主页:tuzhenyu’s page
原文地址:Java多线程系列(三)—生产者消费者模型

(1) 生产者消费者模型

生产者消费者模型关注以下几点:

  • 生产者在存储未满的时候生产,消费者在存储未空的时候消费;

  • 生产者执行生产任务之后会唤醒消费者进行消费,消费者执行消费任务后会唤醒生产者进行生产;

生产者消费者模型的优点:

  • 生产与消费线程之间的解耦,生产者消费者模型将生产者和消费者之间的强耦合解开,变为了生产者和缓冲区/消费者和缓冲区之间的弱耦合;

  • 通过平衡生产者和消费者的处理能力节约CPU资源提高处理数据的速度,如果生产者消费者的处理速度不均衡就会造成CPU时间片白白等待,通过定义不同数量的生产者消费者线程可以缓解这种情况;

(2) 实现生产者消费者模型的方法

  • wait()/notify()/notifyAll()方法

  • lock和condition的await() / signal()方法方法

  • blockingQueue阻塞队列方法

(3) wait/notify实现生产者消费者模型

  • 定义仓库类Depot
public class Depot {    private int capacity;    private int size;    public Depot(int capacity){        this.capacity = capacity;        size = 0;    }    public synchronized void produce(){        try {            while (size>=capacity)                wait();            size++;            System.out.println("生产者生产一个,当前数量为:"+size);            notify();        }catch (Exception e){            e.printStackTrace();        }    }    public synchronized void consume(){        try {            while (size<=0)                wait();            size--;            System.out.println("消费者消费一个,当前数量为:"+size);            notify();        }catch (Exception e){            e.printStackTrace();        }    }}
  • 定义生产者线程
public class ProduceThread extends Thread{    private Depot depot;    public ProduceThread(Depot depot){        this.depot = depot;    }    @Override    public void run() {        while (true){            depot.produce();        }    }}
  • 定义消费者线程
public class ConsumeThread extends Thread{    private Depot depot;    public ConsumeThread(Depot depot){        this.depot = depot;    }    @Override    public void run() {        while (true){            depot.consume();        }    }}
  • 开启生产者消费者模式
public class Main {    public static void main(String[] args) {        Depot depot = new Depot(10);        ProduceThread produceThread = new ProduceThread(depot);        ProduceThread produceThread2 = new ProduceThread(depot);        ConsumeThread consumeThread = new ConsumeThread(depot);        ConsumeThread consumeThread2 = new ConsumeThread(depot);        produceThread.start();        produceThread2.start();        consumeThread.start();        consumeThread2.start();    }}

(4) lock结合condition实现生产者消费者模型

  • 定义仓库类Depot
public class Depot {    private int capacity;    private int size;    private final Lock lock = new ReentrantLock();    private final Condition notEmpty = lock.newCondition();    private final Condition notFull = lock.newCondition();    public Depot(int capacity){        this.capacity = capacity;        size = 0;    }    public void produce(){        lock.lock();        try {            while (size>=capacity)                notFull.await();            size++;            System.out.println("生产者生产一个,当前数量为:"+size);            notEmpty.signal();        }catch (Exception e){            e.printStackTrace();        }finally {            lock.unlock();        }    }    public void consume(){        lock.lock();        try {            while (size<=0)                notEmpty.await();            size--;            System.out.println("消费者消费一个,当前数量为:"+size);            notFull.signal();        }catch (Exception e){            e.printStackTrace();        }finally {            lock.unlock();        }    }}

(5) 生产者消费者模型的假死状态

  • 使用wait()和notify()方法实现的生产者消费者模式容易出现假死现象,假死指的是全部线程都进入了WAITING状态,那么程序就不再执行任何业务功能了,整个项目呈现停滞状态。

  • 假死现象出现的原因是在多生产者多消费者情况下,notify()方法只能随机唤醒一个等待的线程,如果是生产者线程唤醒其他生产者线程或者消费者线程唤醒消费者线程就可能出现假死现象;

  • 假死现象出现的一种情况是:比方说有生产者A和生产者B,缓冲区由于空了,全部消费者线程处于WAITING。生产者B处于WAITING,生产者A被消费者通知生产,生产者A生产出来的产品本应该通知消费者,结果通知了生产者B,生产者B被唤醒,发现缓冲区满了,于是继续WAITING。至此,两个生产者线程处于WAITING,消费者处于WAITING,系统假死。

  • 解决假死现象的方法:

    • 通过notifyAll()方法唤醒所有线程包括生产者线程和消费者线程,唤醒后如果未竞争到锁则会进入阻塞队列;

    • 通过lock和condition,对生产者和消费者创建不同的condition对象,这样就能生产者唤醒消费者,消费者唤醒生产者;