面试官:阻塞队列有了解过吗?

开发 后端
​下面我们就一起看一下Java中给我们提供了哪些好用的工具,先给大家介绍一下BlockingQueue, 本节我们主要讲它的用法。

前言

本节就一起探讨一下什么是阻塞队列,一起来看下吧!

什么是阻塞队列

我们先说一下为什么要引入阻塞队列。我们知道服务器的资源是有限的,就拿典型的生产者和消费者模型来讲。

消费者如果没有东西可以消费了,但是它还是在一直执行,这无疑是在浪费系统资源,所以我们需要阻塞消费者,换过来同理。

生产者没有东西可生产了或者说没有地方存储它生产的东西,这时候我们就需要阻塞生产者。

然而,在开发中,这种模型往往运行在多线程环境中,需要对资源共享从而达到更高的性能,但这样也会造成线程安全问题,比如死锁、重复消费 所以阻塞队列就是帮我们解决这些问题的。

BlockingQueue

下面我们就一起看一下Java中给我们提供了哪些好用的工具,先给大家介绍一下BlockingQueue, 本节我们主要讲它的用法。

BlockingQueue是Java util.concurrent包下的类,BlockingQueue提供了「线程安全的队列访问方式」,并发包下很多高级同步类的实现都是基于BlockingQueue实现的。

BlockingQueue一般用于生产者-消费者模式,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。「BlockingQueue就是存放元素的容器」。

public interface BlockingQueue<E> extends Queue<E> {....}

它本身是一个接口,我们看下它常用的实现类。

ArrayBlockingQueue

一个用数组实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序, 支持公平锁和非公平锁。

  • add() 添加元素。
// 初始化 容量为3
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.add("1"));
System.out.println(queue.add("2"));
System.out.println(queue.add("3"));
System.out.println(queue.add("4"));

我们发现添加到4的时候,报错了Exception in thread "main" java.lang.IllegalStateException: Queue full。

  • remove() 移除元素,如果为空,会返回异常。
// 初始化 容量为3
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.add("1"));
System.out.println(queue.add("2"));
System.out.println(queue.add("3"));
// System.out.println(queue.add("4"));
System.out.println(queue.remove());
System.out.println(queue);

打印:

true
true
true
1
[2, 3]

从结果来看,符合FIFO规则,如果我想移除指定元素怎么办呢?很简单,使用remove("3")。

  • offer() 和add方法类似,但是它不会报错,它会返回false, offer(e,time,unit)支持超时。
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.offer("1"));
System.out.println(queue.offer("2"));
System.out.println(queue.offer("3"));
System.out.println(queue.offer("4"));
true
true
true
false
  • poll() 与remove相似,但它不会返回异常而是返回null, poll(time,unit)。
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.poll());
null
  • take() 与 poll类似, 但它会造成线程阻塞。
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.take());
}

运行后,我们发现程序被阻塞了。

  • put() 添加元素,当容器满的时候,会造成线程阻塞。
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
queue.put("1");
queue.put("2");
}

运行后发现,执行到2的时候被阻塞了。

以上是它的常用方法,其它的实现类就不一一给大家演示了,方法差不多,都是基于BlockingQueue接口,可以自己试着运行一下看看。

LinkedBlockingQueue

一个由链表结构组成的有界队列,此队列的长度为Integer.MAX_VALUE。

SynchronousQueue

是一个不存储元素的阻塞队列,每一个put操作必须等待take操作,否则不能添加元素。支持公平锁和非公平锁。SynchronousQueue的一个使用场景是在线程池里。Executors.newCachedThreadPool()就使用了SynchronousQueue,这个上节我们讲线程复用的时候遇到过。

LinkedTransferQueue

是一个由链表结构组成的无界阻塞队列,相当于其它队列,LinkedTransferQueue队列多了transfer和tryTransfer方法。

PriorityBlockingQueue

是一个支持线程优先级排序的无界队列,默认自然序进行排序,也可以自定义实现compareTo()方法来指定元素排序规则,不能保证同优先级元素的顺序。

DelayQueue

一个实现PriorityBlockingQueue实现延迟获取的无界队列,在创建元素时,可以指定多久才能从队列中获取当前元素。只有延时期满后才能从队列中获取元素,在缓存设计和定时任务调度中经常会遇到。

结束语

本节主要给大家介绍了常用的阻塞队列以及它的基本使用。

责任编辑:姜华 来源: 今日头条
相关推荐

2022-06-30 14:31:57

Java阻塞队列

2022-08-02 06:31:32

Java并发工具类

2022-07-26 08:40:42

Java并发工具类

2022-07-11 10:47:46

容器JAVA

2022-06-10 13:56:42

Java

2022-07-18 14:18:26

Babel代码面试

2022-06-09 11:20:44

volatile关键字

2022-06-15 15:14:17

Java公平锁非公平锁

2022-06-08 13:54:23

指令重排Java

2021-04-12 21:34:29

Redis故障数据

2022-06-24 06:43:57

线程池线程复用

2024-09-03 07:58:46

2020-09-26 22:04:32

数据安全传输HTTPSHTTP 协议

2024-01-26 13:16:00

RabbitMQ延迟队列docker

2020-10-08 14:15:15

Zookeeper

2024-09-09 08:30:56

代码

2019-12-25 11:22:19

负载均衡集群算法

2023-09-26 07:49:11

AOP代理spring

2022-06-02 09:29:55

线程组线程树状结构

2024-05-29 14:34:07

点赞
收藏

51CTO技术栈公众号