Java面试——阻塞队列

阻塞队列

定义:顾名思义,首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下
Java面试——阻塞队列_第1张图片
当阻塞队列是空时,从队列中获取元素的操作会被阻塞。
当阻塞队列是满时,往队列里添加元素的操作将会被阻塞。
这个是消息队列的底层原理

优点:可以不用管阻塞或者唤醒,阻塞队列会解决,可以很简单的写出一个生产者消费者模式。

java实现类架构梳理

BlockingQueue是一个接口类,具体的实现类有以下7中:

ArrayBlockingQueue; 由数组结构组成的有界阻塞队列
LinkedBlockingDeque;由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列

PriorityBlockingQueue;支持优先级排序的无界阻塞队列
DelayQueue;使用优先级队列实现的延迟无阻塞队列
SynchronousQueue;不存储元素的阻塞队列,也即单个元素的队列
LinkedTransferQueue;由链表结构组成的无界阻塞队列。
LinkedBlocking==Deque ==;由链表结构组成的双向阻塞队列

具体使用方法

方法类型 抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e,time,unit)
移除 remove() poll() take() poll(time,unit)
检查 element peek 不可用 不可用

1、就是抛异常的方法在插入满了之后,会报一个异常,remove一样,element是检查队头的元素或者是否为空。
2、特殊值的方法是在插入满之后返回值变成了false而不是一个异常,取出失败的时候返回null。
3、阻塞方法是在插入满之后把这个方法阻塞,一直等待队列空出来一个之后再进行加入,会出现一直等待,也可能出现饥饿现象。
4、超时方法的话,当阻塞队列满时,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出。

什么情况下应用?

  1. 生产者消费者模式
    传统版的代码:
class ShareData{ //资源类
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void increment()throws Exception{

        lock.lock();
        try{
            //1 判断
            while (number!=0){
                //等待不能生产
                condition.await();
            }

            //干活
            number++;
            System.out.println(Thread.currentThread().getName()+"\t "+number);
            //通知唤醒
            condition.signalAll();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }

    }
    public void decrement()throws Exception{

        lock.lock();
        try{
            //1 判断
            while (number==0){
                //等待不能生产
                condition.await();
            }
            //干活
            number--;
            System.out.println(Thread.currentThread().getName()+"\t "+number);
            //通知唤醒
            condition.signalAll();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }

}

/**
 * @author shihangqi
 * @date 2019/10/25 - 14:42
 *
 * 题目:一个初始值为零的变量,两个线程对交替操作,一个加1一个减1,来5轮
 *
 * 1    线程    操作    资源类
 * 2    判断    干活    通知
 * 3    防止虚假唤醒   (在多线程中尽量用while不用if)虚假唤醒主要是因为
 *
 */


public class ProdConsumer_TraditionDemo {

    public static void main(String[] args) {

        ShareData shareData = new ShareData();

        new Thread(()->{
            for(int i = 1; i <=5; i++){
                try {
                    shareData.increment();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"aa").start();
        new Thread(()->{
            for(int i = 1; i <=5; i++){
                try {
                    shareData.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"bb").start();
    }

}

阻塞队列版的代码:

/**
 * @author shihangqi
 * @date 2019/11/5 - 10:05
 */

class MyResource{
    private volatile boolean FLAG = true;
    private AtomicInteger atomicInteger = new AtomicInteger();

    BlockingQueue<String> blockingQueue = null;

    public MyResource(BlockingQueue<String> blockingQueue) {

        this.blockingQueue = blockingQueue;
        System.out.println(blockingQueue.getClass().getName());
    }

    public void myProd()throws Exception{
        String data = null;
        boolean retValue;
        while (FLAG){
            data = atomicInteger.incrementAndGet()+"";
            retValue = blockingQueue.offer(data,2, TimeUnit.SECONDS);
            if (retValue){
                System.out.println(Thread.currentThread().getName()+"\t 插入队列"+data+"成功");
            }else {
                System.out.println(Thread.currentThread().getName()+"\t 插入队列"+data+"失败");
            }
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e){ e.printStackTrace(); }
        }
        System.out.println(Thread.currentThread().getName()+"\t 大老板叫停了,FLAG=false了");
    }


    public void myConsumer()throws Exception{
        String result = null;
        while (FLAG){
            result = blockingQueue.poll(2,TimeUnit.SECONDS);
            if (null == result || result.equalsIgnoreCase("")){
                FLAG = false;
                System.out.println(Thread.currentThread().getName()+"\t 超过2秒钟没有取到蛋糕,消费退出");
                return;
            }
            System.out.println(Thread.currentThread().getName()+"\t 消费队列"+result+"成功");
        }
    }

    public void stop(){
        this.FLAG = false;
    }

}


public class ProdConsumer_BlockQueueDemo {

    public static void main(String[] args) {
        MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t 生产线程启动");
            try {
                myResource.myProd();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"Prod").start();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t 消费线程启动");
            try {
                myResource.myConsumer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"Consumer").start();


        try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e){ e.printStackTrace(); }

        System.out.println("5秒过了叫停");
        myResource.stop();
    }
}

你可能感兴趣的:(面试集锦,BlockingQueue,生产者消费者模式,阻塞队列)