java中的阻塞队列


前言

在多线程领域,所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动呗唤醒。
为什么需要阻塞队列?
好处是我们不需要关心什么时候需要阻塞线程,什么手需要唤醒线程,因为这一切阻塞队列都一手包办了,在concurrent包发布之前,在多线程环境下,我们每个程序员都必须要自己控制这些细节,尤其还要兼顾效率和线程安全,而这给我们的程序带来不小的复杂度。


提示:以下是本篇文章正文内容,下面案例可供参考

一、阻塞队列的种类

在这里插入图片描述
BlockingQueue为阻塞队列的顶级接口,下边有几种阻塞队列:

ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
LinkedTransferQueue:由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:由链表组成的双向阻塞队列。

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
在这里插入图片描述
首先看一下抛出异常的代码:

public class BlockQueueDemo {
    public static void main(String[] args) {
        // 创建阻塞队列,3为阻塞队列的容量
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.add("a")); // true
        System.out.println(blockingQueue.add("b")); // true
        System.out.println(blockingQueue.add("c")); // true

        // 打开此行 报错 Queue full
        // System.out.println(blockingQueue.add("x")); // 队列已满,不能再添加

        // 查看阻塞队列里边是否有元素,有的话拿出第一个元素 a, 没有的话报错 NoSuchElementException
        System.out.println(blockingQueue.element()); // a 

        System.out.println(blockingQueue.remove()); // a
        System.out.println(blockingQueue.remove()); // b
        System.out.println(blockingQueue.remove()); // c

        // 打开此行 报错 NoSuchElementException
//        System.out.println(blockingQueue.remove()); // 已经没有元素,不能再移除
    }
}

看一下特殊值的代码:

public class BlockQueueDemo {
    public static void main(String[] args) {
        // 创建阻塞队列,3为阻塞队列的容量
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer("a")); // true
        System.out.println(blockingQueue.offer("b")); // true
        System.out.println(blockingQueue.offer("c")); // true

        // 打开此行 不会和add方法一样报错 Queue full
         System.out.println(blockingQueue.offer("x")); // false

        // 查看阻塞队列里边是否有元素,有的话拿出第一个元素 a, 没有的话报错 返回null
        System.out.println(blockingQueue.peek()); // a

        System.out.println(blockingQueue.poll()); // a
        System.out.println(blockingQueue.poll()); // b
        System.out.println(blockingQueue.poll()); // c

        // 打开此行 
        System.out.println(blockingQueue.poll()); // null
    }
}

阻塞就是当队列满了的时候,生产者线程在put的时候,队列会一直阻塞生产线程,直到put数据或者响应中断退出,当队列空的时候,消费者线程在take的时候,队列会一直阻塞消费者线程直到队列可用。

public class BlockQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        // 创建阻塞队列,3为阻塞队列的容量
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        // 返回false
//        blockingQueue.put("x");
        blockingQueue.take();
        blockingQueue.take();
        blockingQueue.take();

        // 返回null
//        blockingQueue.take();

    }

超时:

public class BlockQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        // 创建阻塞队列,3为阻塞队列的容量
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

		// 表示2秒后队列中的值就会失效被移除
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS)); // true
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS)); // true
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS)); // true
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS)); // 两秒后返回false

    }
}

在这里插入图片描述

二、SynchronousQueue

特性

SynchronousQueue队列中只能允许有一个元素,只有元素从队列中拿出来才允许再往里边放。下边看一下代码。大家可以自己运行感受一下。

public class BlockQueueDemo1 {
    public static void main(String[] args) throws InterruptedException {
        // 创建阻塞队列
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " >>> put");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName() + " >>> put");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName() + " >>> put");
                blockingQueue.put("3");
            }catch (Exception e) {
                e.printStackTrace();
            }
        },"A").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(blockingQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(blockingQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(blockingQueue.take());
            }catch (Exception e) {
                e.printStackTrace();
            }
        },"B").start();

    }
}

二、使用

以生产者消费者的例子来写一个阻塞队列的demo,场景是生产者每秒生产一个,消费者每秒消费一个,两者交替执行,持续5秒。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class BlockQueueDemo2 {
    public static void main(String[] args) throws InterruptedException {
        MyResource myResource = new MyResource(new ArrayBlockingQueue(10));

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "生产线程启动");
            try {
                myResource.myProd();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"prod").start();

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

        TimeUnit.SECONDS.sleep(5);
        System.out.println("main线程停止");

        myResource.stop();

    }
}

class MyResource {
    private volatile boolean FLAG = true;
    private AtomicInteger num = new AtomicInteger();
    private BlockingQueue<String> blockQueue;

    public MyResource(BlockingQueue blockQueue) {
        this.blockQueue = blockQueue;
        System.out.println(blockQueue.getClass().getName());
    }

    public void myProd () throws InterruptedException {
        String data;
        boolean retValue;
        while (FLAG) {
            data = num.incrementAndGet() + "";
            retValue = blockQueue.offer(data, 2L, TimeUnit.SECONDS);
            if (retValue) {
                System.out.println(Thread.currentThread().getName() + "插入队列"+ data +"成功");
            } else {
                System.out.println(Thread.currentThread().getName() + "插入队列"+ data +"失败");
            }
            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println(Thread.currentThread().getName() + "主程序叫停,表示FLAG=false, 生产动作结束");
    }


    public void myConsumer () throws InterruptedException {
        String result = null;
        while (FLAG) {
            result = blockQueue.poll(2L, TimeUnit.SECONDS);
            if (result == null || result.equalsIgnoreCase("")) {
                FLAG = false;
                System.out.println(Thread.currentThread().getName() + "超过两秒钟没有消费,退出");
                return;
            }
            System.out.println(Thread.currentThread().getName() + "消费队列"+ result +"成功");
        }
        System.out.println(Thread.currentThread().getName() + "主程序叫停,表示FLAG=false, 生产动作结束");
    }

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

壹升茉莉清

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值