生产者消费者模式详解

前言
本篇详细讲解生产者消费者模式的几种实现方式。

  • 使用object.wait()/object.notify()方式实现
  • condition.await()/signal()方式实现
  • BlockingQueue阻塞队列的方式实现

使用object.wait()/object.notify()方式实现

下面我们直接上代码,通过一个案例来展示。

public class ConsumeTest2 {

    private static final ArrayList<String> list = new ArrayList<>(5);

    public static void main(String[] args) {
        new Thread(new ConsumerImpl(), "consume thread").start();
        new Thread(new ProductImpl(), "product thread").start();
    }

    static class ConsumerImpl implements Runnable {

        @Override
        public void run() {
            while (true) {
                synchronized (list) {
                    if (list.size() == 0) {
                        try {
                            System.out.println("size = 0, consume wait");
                            list.wait();
                            System.out.println("consume wait finish");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String removeName = list.remove(0);
                    System.out.println("消费了:" + removeName + "--剩余产品数量:" + list.size());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    list.notify();
                }
            }
        }
    }

    static class ProductImpl implements Runnable {

        @Override
        public void run() {
            while (true) {
                synchronized (list) {
                    if (list.size() == 5) {
                        try {
                            System.out.println("size = 5, product wait");
                            list.wait();
                            System.out.println("product wait finish");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String name = "product:" + (list.size() + 1);
                    list.add(name);
                    System.out.println("添加了产品:" + name + "--剩余空间:" + (5 - list.size()));
                    try {
                        Thread.sleep(1100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    list.notify();
                }
            }
        }
    }
}
//打印信息
size = 0, consume wait
添加了产品:product:1--剩余空间:4
添加了产品:product:2--剩余空间:3
添加了产品:product:3--剩余空间:2
添加了产品:product:4--剩余空间:1
添加了产品:product:5--剩余空间:0
size = 5, product wait
consume wait finish
消费了:product:1--剩余产品数量:4
消费了:product:2--剩余产品数量:3
消费了:product:3--剩余产品数量:2
消费了:product:4--剩余产品数量:1
消费了:product:5--剩余产品数量:0
size = 0, consume wait
  • 我们使用object.wait()/notify()实现生产者消费者模式,需要自己控制生产和消费的条件,并且自己控制锁的释放。源码请点击Forward查看。
  • object.wait和object.notify方法要成对出现,notify方法只会随机唤醒一个线程,而notifyAll()会唤醒所有的线程。
  • object的wait方法会释放锁,但是只能等到此线程执行完毕后才能释放锁。想要调用wait或者notify以及notifyAll方法,线程必须得持有锁对象才可以,关于线程的详细图解可以查看Java线程六种状态图解;

condition.await()/signal()方式实现

public class ConsumeTest3 {

    private static final ArrayList<String> list = new ArrayList<>(5);
    private static Lock lock = new ReentrantLock();
    private static Condition product = lock.newCondition();
    private static Condition consume = lock.newCondition();

    public static void main(String[] args) {
        new Thread(new ConsumerImpl(), "consume thread").start();
        new Thread(new ProductImpl(), "product thread").start();
    }

    static class ConsumerImpl implements Runnable {

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    if (list.size() == 0) {
                        try {
                            System.out.println("size = 0, consume wait");
                            consume.await();
                            System.out.println("consume wait finish");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String removeName = list.remove(0);
                    System.out.println("消费了:" + removeName + "--剩余产品数量:" + list.size());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    product.signal();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    static class ProductImpl implements Runnable {

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    if (list.size() == 5) {
                        try {
                            System.out.println("size = 5, product wait");
                            product.await();
                            System.out.println("product wait finish");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String name = "product:" + (list.size() + 1);
                    list.add(name);
                    System.out.println("添加了产品:" + name + "--剩余空间:" + (5 - list.size()));
                    try {
                        Thread.sleep(1100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    consume.signal();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}
  • Condition的await和signal以及signalAll()和Object的wait(),notify(),nofityAll()一一对应,在使用上Condition的加锁方式是显示的调用lock.lock和lock.unlock(),而synchronized加锁和解锁的方式是虚拟机实现的,本质上一样,不过我们不能在代码中看到罢了。

BlockingQueue阻塞队列的方式实现

public class ConsumeTest4 {

    private static final ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(5);

    public static void main(String[] args) {
        new Thread(new ConsumerImpl(), "consume thread").start();
        new Thread(new ProductImpl(), "product thread").start();
    }

    static class ConsumerImpl implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    String removeName = queue.take();
                    System.out.println("消费了:" + removeName + "--剩余产品数量:" + queue.size());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class ProductImpl implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    String name = "product:" + (queue.size() + 1);
                    queue.put(name);
                    System.out.println("添加了产品:" + name + "--剩余空间:" + (5 - queue.size()));
                    Thread.sleep(1100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 使用阻塞队列的方式很简单,是因为内部已经处理了关于锁和队列的问题,我们可以在源码中一窥究竟:
    public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    
    	/** Main lock guarding all access */
        final ReentrantLock lock;
    
        /** Condition for waiting takes */
        private final Condition notEmpty;
    
        /** Condition for waiting puts */
        private final Condition notFull;
        
        public ArrayBlockingQueue(int capacity, boolean fair) {
            if (capacity <= 0)
                throw new IllegalArgumentException();
            this.items = new Object[capacity];
            lock = new ReentrantLock(fair);
            notEmpty = lock.newCondition();
            notFull =  lock.newCondition();
        }
        
    	public void put(E e) throws InterruptedException {
            Objects.requireNonNull(e);
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                while (count == items.length)
                    notFull.await();
                enqueue(e);
            } finally {
                lock.unlock();
            }
        }
    	
    	public E take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                while (count == 0)
                    notEmpty.await();
                return dequeue();
            } finally {
                lock.unlock();
            }
        }
    }
    
    • 由上面源码可知,ArrayBlockingQueue内部的实现方式正是我们第二种的方式,所以在本质上他们是一致的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值