阻塞队列

  • 当阻塞队列是空时,从队列获取元素的操作将会被阻塞

  • 当阻塞队列是满时,往队列里添加元素的操作将会被阻塞

阻塞队列的优点

我们不需要关心什么时候阻塞线程,什么时候需要唤醒线程

在concurrent包发布以前,在多线程环境下,我们每个程序员都必须自己取控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

BlockingQueue的核心方法

  1. 异常:

    ​ NoSuchException

  2. 特殊值: true、false

  3. 阻塞: 一直等待直到put成功,或者响应中断退出

  4. 超时: 阻塞一定时间,超时后退出

阻塞队列的种类

BlockingQueue阻塞队列是属于一个接口,底下有七个实现类

  1. ArrayBlockingQueue:由数组结构组成的有界阻塞队列

  1. LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列,但是界限非常大,相当于无界,可以当成无界

  2. PriorityBlockingQueue:支持优先级排序的无界阻塞队列

  3. DelayQueue:使用优先级队列实现的延迟无界阻塞队列

  4. SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列

    SynchronousQueue没有容量,与其他BlockingQueue不同,SynchronousQueue是一个不存储的BlockingQueue,每一个put操作必须等待一个take操作,否者不能继续添加元素

  5. LinkedTransferQueue:由链表结构组成的无界阻塞队列

  6. LinkedBlokingDeque:由链表结构组成的双向阻塞队列

阻塞队列用途

  1. 生产者消费者
  • 传统写法

    /**
     * 题目:一个初始值为0的变量,两个线程对其交替操作,一个加1一个减1
     * 1 线程    操作        资源类
     * 2 判断    干活        通知
     * 防止虚假唤醒
     */
    public class ProdConsumer_TraditionDemo {
        public static void main(String[] args){
            ShareData shareData = new ShareData();
            //A:++
            new Thread(() ->{
                for(int i = 0; i < 5; i++){
                    try {
                        shareData.increment();
                    }catch (Exception e){
                        e.getStackTrace();
                    }
                }
            },"A").start();
            //B--
            new Thread(() ->{
                for(int i = 0; i < 5; i++){
                    try {
                        shareData.dcrement();
                    }catch (Exception e){
                        e.getStackTrace();
                    }
                }
            },"B").start();
        }
    }
    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) {
                    //等待,不能生产 为了防止出现虚假唤醒机制,不能使用if来进行判断,而应该使用while
                    condition.await();
                }
                //干活
                number++;
                System.out.println(Thread.currentThread().getName() + "\t" + number);
                //通知,唤醒
                condition.signalAll();
            } catch (Exception e) {
                e.getStackTrace();
            } finally {
                //解锁
                lock.unlock();
            }}
            public void dcrement ()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.getStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    
  • 输出:

  • 阻塞队列版

    class MyResource{
        //默认开启,进行生产和消费
        //用volatile保证可见性
        private volatile boolean FLAG = true;
        //用原子类代替number++
        private AtomicInteger atomicInteger = new AtomicInteger();
        BlockingQueue<String> blockingQueue = null;
        //通过构造函数确定class
        public MyResource(BlockingQueue<String> blockingQueue) {
            this.blockingQueue = blockingQueue;
            System.out.println(blockingQueue.getClass().getName());
        }
        //生产
        public void myProd()throws Exception{
            String data = null;
            boolean retValue = false;
            while (FLAG){
                //以原子方式将当前值加 1
                data = atomicInteger.incrementAndGet()+"";
                //每2s插入一个数据进入队列中
                //
                retValue = blockingQueue.offer(data,2L, TimeUnit.SECONDS);
                if(retValue){
                    System.out.println(Thread.currentThread().getName()+"\t插入队列成功,数据为:"+data);
                }else{
                    System.out.println(Thread.currentThread().getName()+"\t插入队列失败");
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("PRO:flag = false ,stop");
        }
        //消费者
        public void myConsumer()throws Exception{
            String result = null;
            while (FLAG){
                //每2s取出一个数据
                result = blockingQueue.poll(2L,TimeUnit.SECONDS);
                //等待2s,取出result为空,返回null
                if(result == null && result.equalsIgnoreCase("")){
                    FLAG = false;
                    System.out.println(Thread.currentThread().getName() + "\t 消费失败,队列中已为空,退出" );
                    return;
                }
                System.out.println(Thread.currentThread().getName()+"\t 消费成功,取出数据为: "+result);
            }
        }
        //定义一个手动停止生产和消费的方法
        public void stop(){
            this.FLAG = false;
        }
    }
    /**
     * volatile/CAS/atomicInteger/BlockQueue/线程交互/原子引用
     */
    public class ProdConsumer_BlockQueueDemo {
        public static void main(String[] args){
            //  传入具体的实现类, ArrayBlockingQueue
            MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
    
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"\t 生产线程启动");
                try {
                    myResource.myProd();
                    System.out.println();
                    System.out.println();
                }catch (Exception e){
                    e.getStackTrace();
                }
            },"Prod").start();
    
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"\t 消费线程启动");
                try {
                    myResource.myConsumer();
                }catch (Exception e){
                    e.getStackTrace();
                }
            },"Consumer").start();
            //5s后手动停止生产消费
            try{
                TimeUnit.SECONDS.sleep(5);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println();
            System.out.println();
            System.out.println("老板:stop");
            myResource.stop();
        }
    }
    
    • 输出:
  1. 线程池

  2. 消息中间件

posted @ 2022-02-20 16:30  ftfty  阅读(63)  评论(0编辑  收藏  举报