生产者消费者模型显式锁版

 

缓冲区

public class Storage {
    // 仓库最大存储量
    private final int MAX_SIZE = 100;
    // 仓库存储的载体
    private LinkedList<Object> list = new LinkedList<Object>();
    // 锁
    private final Lock lock = new ReentrantLock();
    // 仓库满的条件变量
    private final Condition full = lock.newCondition();
    // 仓库空的条件变量
    private final Condition empty = lock.newCondition();

    // 生产产品
    public void produce() {
    	 // 加锁
        lock.lock();
        // 满了
        while (list.size() == MAX_SIZE) {
            try {
                full.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 不满
        list.add(new Object());
        // 唤醒empty阻塞队列中的线程
        empty.signalAll();
        // 释放锁
        lock.unlock();
    }

    // 消费产品
    public void consume() {
        // 加锁
        lock.lock();
        // 空了
        while (list.size() == 0) {
            try {
                empty.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 不空
        list.remove();
        // 唤醒full阻塞队列中的线程
        full.signalAll();
        // 释放锁
        lock.unlock();
    }
}

生产线程

public class Producer extends Thread {

    private Storage storage;

    public Producer(Storage storage){
        this.storage = storage;
    }

    @Override
    public void run(){
        storage.produce();
    }
}

消费线程

public class Consumer extends Thread {

    private Storage storage;

    public Consumer(Storage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        storage.consume();
    }
}

关键点注释

模型由3部分构成:缓冲区、生产线程、消费线程。其中最关键的是缓冲区的编写。

由于频繁做add/remove操作,所以应该使用链式存储的LinkedList、LinkedHashMap等结构。

分别为“满了”和“空了”创建阻塞队列。当“满了”时,把调用(生产)线程加入“满了”阻塞队列;当“空了”时,把调用(消费)线程加入“空了”阻塞队列。当“不满”时,唤醒“空了”阻塞队列中的(消费)线程;当“不空”时,唤醒“满了”阻塞队列中的(生产)线程。

生产方法和消费方法结构差不多,记住1个也就记住了另1个。

  1. 临界区为整个方法;
  2. while(条件变量),条件变量是缓冲区满了或空了;
  3. while中await()交出CPU资源;
  4. while后执行add/remove操作;
  5. signalAll()唤醒相反的等待线程。