01-PC模式的几种实现(面试题)

生产者消费者模式

一、synchronized同步

  • 代码
public class PcSync {

    static int MAX = 10;
    static Queue<Object> queue = new ArrayDeque<>(MAX);

    public static void main(String[] args) {

        Producer p = new Producer();
        Consumer c1 = new Consumer();
        Consumer c2 = new Consumer();
        new Thread(p).start();
        new Thread(p).start();
        new Thread(c1).start();
        new Thread(c2).start();
    }


    static class Producer implements Runnable {

        @Override
        public void run() {
            while (true) {
                synchronized (PcSync.class) {
                    if (queue.size() < MAX) {
                        queue.add(new Object());
                        System.out.println("生产一个,生产后有商品" + queue.size() + "个");
                        SleepTools.randomMs(400);
                    } else {
                        System.out.println("满了,不能再生产了...");
                    }
                }
            }
        }
    }

    static class Consumer implements Runnable {

        @Override
        public void run() {
            while (true) {
                synchronized (PcSync.class) {
                    if (queue.size() > 0) {
                        queue.remove();
                        System.out.println("消费一个,消费后有商品" + queue.size() + "个");
                        SleepTools.randomMs(400);
                    } else {
                        System.out.println("空了,不能再消费了...");
                    }
                }
            }
        }
    }
}

二、wait-notify

  • 代码
public class PcWaitNotify {

    static int MAX = 10;
    static Queue<Object> queue = new ArrayDeque<>(MAX);

    public static void main(String[] args) {
        Producer p = new Producer();
        Consumer c1 = new Consumer();
        Consumer c2 = new Consumer();
        new Thread(p).start();
        new Thread(p).start();
        new Thread(c1).start();
        new Thread(c2).start();
    }


    static class Producer implements Runnable {

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    if (queue.size() >= MAX) {
                        queue.notifyAll();
                    } else {
                        queue.add(new Object());
                        System.out.println("生产者生产了 1 个,现在有:" + queue.size() + " 个商品...");
                        SleepTools.randomMs(500);
                    }
                }
            }
        }
    }

    static class Consumer implements Runnable {

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    if (queue.size() <= 0) {
                        queue.notifyAll();
                    } else {
                        queue.remove();
                        System.out.println("消费者消费了 1 个,现在有:" + queue.size() + " 个商品...");
                        SleepTools.randomMs(500);
                    }
                }
            }
        }
    }
}

三、Condition

  • 代码
public class PcCondition {

    private static ReentrantLock lock = new ReentrantLock();
    private static Condition consumer = lock.newCondition();
    private static Condition producer = lock.newCondition();
    private static LinkedList<Object> buffer = new LinkedList<>();
    private static final int MAX = 5;

    public static void main(String[] args) {
        new Thread(new Producer()).start();
        new Thread(new Producer()).start();
        new Thread(new Consumer()).start();
        new Thread(new Consumer()).start();
    }


    static class Producer implements Runnable {

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (buffer.size() >= MAX) {
                        System.out.println("满了...");
                        try {
                            producer.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (buffer.size() < MAX) {
                        buffer.add(new Object());
                        System.out.println("生产一个...生产后数量是" + buffer.size());
                        consumer.signalAll();
                        SleepTools.randomMs(5000);
                    }
                } finally {
                    lock.unlock();
                }
                //避免第一次生产者生产之后,一直重复获取到所,导致满了之后才会有消费者开始消费
                SleepTools.randomMs(5);
            }
        }
    }

    static class Consumer implements Runnable {

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (buffer.size() <= 0) {
                        System.out.println("空了...");
                        try {
                            consumer.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (buffer.size() > 0) {
                        buffer.removeFirst();
                        System.out.println("消费一个...消费后数量是" + buffer.size());
                        producer.signalAll();
                        SleepTools.randomMs(5000);
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

四、同步队列

  • 代码
public class Pc {

    private static AtomicInteger integer = new AtomicInteger(0);

    public static void main(String[] args) {
        //BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(100);
        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>(100);
        Producer p = new Producer(blockingQueue);
        Consumer c1 = new Consumer(blockingQueue);
        Consumer c2 = new Consumer(blockingQueue);
        new Thread(p).start();
        new Thread(p).start();
        new Thread(c1).start();
        new Thread(c2).start();
    }


    static class Producer implements Runnable {

        BlockingQueue<Integer> blockingQueue;

        Producer(BlockingQueue blockingQueue) {
            this.blockingQueue = blockingQueue;
        }

        @Override
        public void run() {
            while (true) {
                int i = integer.getAndIncrement();
                blockingQueue.add(i);
                System.out.println("生产者生产商品,编号为" + i);
                SleepTools.randomMs(5000);
            }
        }
    }

    static class Consumer implements Runnable {

        BlockingQueue<Integer> blockingQueue;

        Consumer(BlockingQueue blockingQueue) {
            this.blockingQueue = blockingQueue;
        }

        @Override
        public void run() {
            while (true) {
                Integer element = null;
                try {
                    element = blockingQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者消费一个,编号为" + element);
                SleepTools.randomMs(5000);
            }
        }
    }
}

五、有bug的情况

  • 代码

/**
 * @author by mozping
 * @Classname PcCondition
 * @Description TODO
 * 在使用singal的时候会出现这样的问题:
 * 1.某一时刻,生产者生产了之后,商品满了,然后生产者await等待被唤醒,将锁释放;
 * 2.消费者1拿到锁,然后消费掉商品,再调用singal,消费者1将锁释放
 * 3.消费者2被消费者1的singal唤醒,发现没有可消费的商品,然后就await等待自己被生产者唤醒,消费者2释放锁
 * 4.消费者1拿到锁,发现不能消费,也调用await,这样生产者和2个消费者都阻塞了
 *
 * 1.这里的原因在于singal唤醒了同类,即第2步到第3步唤醒了同类导致生产者没有被唤醒,我自己验证用singalAll没有死锁的问题,这回唤醒全部等待在一个condition的线程
 * 2.第二个方法是在await之前发生singal信号,不过最好还是使用singalAll,这样即使自己要阻塞了也会唤醒其他的线程避免像前面的第四步* 那样全部阻塞
 * 3.最推荐的方法是第三个,就是使用2个Condition,生产者和消费者等待在不同的Condition对象,因此不会出现唤醒同理的情况
 *  @Date 2019/8/12 17:36
 */
public class PcConditionBug {

    private static ReentrantLock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    private static LinkedList<Object> buffer = new LinkedList<>();
    private static final int MAX = 5;

    public static void main(String[] args) {
        new Thread(new Producer()).start();
        new Thread(new Producer()).start();
        new Thread(new Consumer()).start();
        new Thread(new Consumer()).start();
    }


    static class Producer implements Runnable {

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (buffer.size() >= MAX) {
                        System.out.println("满了...");
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (buffer.size() < MAX) {
                        buffer.add(new Object());
                        System.out.println("生产一个...生产后数量是" + buffer.size());
                        condition.signal();
                        SleepTools.randomMs(50);
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    static class Consumer implements Runnable {

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (buffer.size() <= 0) {
                        System.out.println("空了...");
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (buffer.size() > 0) {
                        buffer.removeFirst();
                        System.out.println("消费一个...消费后数量是" + buffer.size());
                        condition.signal();
                        SleepTools.randomMs(50);
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

对比

方案优点缺点
synchronized代码简单,方法被同步执行,线程安全相对没那么灵活
wait-notify容易学习了解同步机制需要注意死锁的情况,用singal要小心
Condition容易控制注意自由一个Condition的时候有可能会死锁
同步队列不需要考虑线程安全,线程安全容器实现了线程间的同步,编码简单不利于了解同步细节
  • 如果要实现安全高效的,建议使用同步队列,另外双Condition也很好
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值