Java中ArrayBlockingQueue和LinkedBlockingQueue

简介:

Java中的BlockingQueue是java.util.concurrent包中的一个接口,是JDK中的并发工具,提供了线程安全的队列,可以用来协调生产者与消费者线程的生产和消费的速度,并且解决了高并发下数据读写的安全问题。BlockingQueue具有阻塞的复杂行为,可以实现生产、消费线程集合的同步。

Java中有两个BlockingQueue的实现类ArrayBlockingQueue和LinkedBlockingQueue,两者都实现了BlockingQueue接口,但是在实现细节上有所不同,下面将对它们进行详细讲解。

一、ArrayBlockingQueue

ArrayBlockingQueue是一个有界阻塞队列,它的容量固定,在创建时必须指定容量大小,并且不可更改。它内部以数组进行实现,因此其读写速度较快。

1.1 初始化

ArrayBlockingQueue的初始化需要指定容量大小,如下所示:

BlockingQueue<String> queue = new ArrayBlockingQueue<>(capacity);

其中capacity表示队列的容量大小。

1.2 队列操作

ArrayBlockingQueue提供了一系列的队列操作方法,例如:

  • add(E e) 将指定元素插入此队列。
  • put(E e) 将指定元素插入此队列,如有必要阻塞线程等待可用。
  • take() 移除并返回此队列的头部,如果此队列为空,则阻塞线程等待可用。
  • poll() 检索并移除此队列的头,如果此队列为空,则返回 null。

示例:

BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);

queue.add("A");
queue.add("B");
queue.add("C");// 添加第三个元素,会抛出IllegalStateException异常

运行结果:

Exception in thread "main" java.lang.IllegalStateException: Queue full
    at java.util.AbstractQueue.add(AbstractQueue.java:98)
    at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)

从上面的运行结果中可以看出,当添加元素达到队列容量时,再次添加元素会抛出IllegalStateException异常。

示例:

BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);

queue.put("A");
queue.put("B");
queue.put("C");// 阻塞线程,直到队列中有空闲位置

String element = queue.take();// 从队列中取出元素,如果队列为空则阻塞线程,直到有可用元素

1.3 等待超时操作

ArrayBlockingQueue还提供了等待超时操作的方法,例如:

  • offer(E e, long timeout, TimeUnit unit) 将指定元素插入此队列,并在指定的等待时间内等待空间变得可用。
  • poll(long timeout, TimeUnit unit) 检索并移除此队列的头,如果此队列为空,则在指定的等待时间内等待可用元素。

示例:

BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);

queue.offer("A", 1000, TimeUnit.MILLISECONDS);
queue.offer("B", 1000, TimeUnit.MILLISECONDS);
queue.offer("C", 1000, TimeUnit.MILLISECONDS);// 阻塞线程,直到队列中有空闲位置,等待1秒

String element = queue.poll(1000, TimeUnit.MILLISECONDS);// 从队列中取出元素,如果队列为空则等待1秒,直到有可用元素

二、LinkedBlockingQueue

LinkedBlockingQueue是一个无界阻塞队列,它的容量没有限制,在创建时可以选择是否指定容量大小。它内部以链表进行实现,因此其读写速度较慢,但是其容量不受限制,可以用于解决任务生产速度与消费速度不一致的场景。

2.1 初始化

LinkedBlockingQueue的初始化可以选择是否指定容量大小,如下所示:

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

或者指定容量大小,例如:

BlockingQueue<String> queue = new LinkedBlockingQueue<>(capacity);

其中capacity表示队列的容量大小,如果不指定则默认为Integer.MAX_VALUE。

2.2 队列操作

LinkedBlockingQueue也提供了一系列的队列操作方法,与ArrayBlockingQueue的方法类似,例如:

  • add(E e) 将指定元素插入此队列。
  • put(E e) 将指定元素插入此队列,如有必要阻塞线程等待可用。
  • take() 移除并返回此队列的头部,如果此队列为空,则阻塞线程等待可用。
  • poll() 检索并移除此队列的头,如果此队列为空,则返回 null。

示例:

BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);

queue.add("A");
queue.add("B");
queue.add("C");// 添加第三个元素,不会抛出异常

String element = queue.poll();// 从队列中取出元素,如果队列为空则返回null

示例:

BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);

queue.put("A");
queue.put("B");
queue.put("C");// 阻塞线程,直到队列中有空闲位置

String element = queue.take();// 从队列中取出元素,如果队列为空则阻塞线程,直到有可用元素

2.3 等待超时操作

LinkedBlockingQueue也提供了等待超时操作的方法,例如:

  • offer(E e, long timeout, TimeUnit unit) 将指定元素插入此队列,并在指定的等待时间内等待空间变得可用。
  • poll(long timeout, TimeUnit unit) 检索并移除此队列的头,如果此队列为空,则在指定的等待时间内等待可用元素。

示例:

BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);

queue.offer("A", 1000, TimeUnit.MILLISECONDS);
queue.offer("B", 1000, TimeUnit.MILLISECONDS);
queue.offer("C", 1000, TimeUnit.MILLISECONDS);// 阻塞线程,直到队列中有空闲位置,等待1秒

String element = queue.poll(1000, TimeUnit.MILLISECONDS);// 从队列中取出元素,如果队列为空则等待1秒,直到有可用元素

总结:

ArrayBlockingQueue和LinkedBlockingQueue都是BlockingQueue的实现类,都可以用来实现生产者和消费者模型,但是它们在实现方式上有所不同。ArrayBlockingQueue是一个有界阻塞队列,内部以数组进行实现,读写速度较快;LinkedBlockingQueue是一个无界阻塞队列,内部以链表进行实现,读写速度较慢。

当生产者的速度大于消费者的速度时,ArrayBlockingQueue可以避免队列无限增长的问题;当生产者和消费者速度相差不大,或者需要无限制地添加元素时,可以选择使用LinkedBlockingQueue。

参考文章:https://www.jianshu.com/p/97cccbb9fc99

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中ArrayBlockingQueue和LinkedBlockingQueue - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • jsp中checkbox用法详解

    JSP中Checkbox用法详解 在JSP中通过Checkbox可以实现多选以及默认选中的功能,下面我们来详细讲解Checkbox的用法。 CheckBox的基本语法 Checkbox的基本语法如下: <input type="checkbox" name="checkName" value="chec…

    Java 2023年6月15日
    00
  • 深入理解Promise.all

    当使用 Promise 处理多个异步操作时,有时候我们需要等待所有操作都完成后再进行下一步操作。Promise.all 就是一个工具,它可以接收一个 Promise 对象数组作为参数,并返回一个新的 Promise 对象,当所有的 Promise 对象都成功返回时,该 Promise 对象的状态为“成功”(fulfilled),返回值是一个数组,数组元素按照…

    Java 2023年5月23日
    00
  • 四种引用类型在JAVA Springboot中的使用详解

    四种引用类型在JAVA Springboot中的使用详解 在Java Springboot中,有四种引用类型:强引用、软引用、弱引用和虚引用。这些引用类型的使用非常广泛,可以帮助我们更好地管理Java应用程序中的内存。下面我们将详细讲解这四种引用类型的使用。 强引用 强引用是我们使用最广泛的一种引用类型,它是默认的引用类型。当我们在代码中创建了一个对象并且将…

    Java 2023年5月19日
    00
  • HttpServletRequest对象方法的用法小结

    HttpServletRequest对象是Java EE中常用的请求对象,表示一个HTTP请求,包含了请求的头部信息、参数、Cookie、Session等。下面我们来详细讲解HttpServletRequest对象方法的用法: 请求行信息 获取HTTP请求的请求URL、请求方式、协议版本、URI、参数等请求行信息,主要包含以下方法: getRequestUR…

    Java 2023年6月15日
    00
  • Java Web 简单的分页显示实例代码

    下面是详细讲解“Java Web 简单的分页显示实例代码”的完整攻略,包括两条示例说明: 1. 分页显示实现原理 在实现分页显示之前,我们需要先了解分页的原理。当我们在页面中点击“下一页”或者“上一页”等翻页按钮时,客户端会向服务器发送请求,请求需要显示的数据的页数及每页显示的数据数量。服务器收到请求后,根据请求参数查询指定页数的数据,返回给客户端,客户端再…

    Java 2023年6月15日
    00
  • Java中static变量能继承吗

    Java中的static变量是类级别的变量,即使类还没有实例化,它也已经存在了。因此,它的值对于类中定义的所有方法和对象实例是相同的。那么,Java中的static变量能否被继承呢?答案是可以。 当一个子类继承一个父类时,它包含了父类的所有非私有成员变量和方法。这些变量和方法可以被直接访问,但是对于static变量,Java有一些额外的规则需要遵循。下面通过…

    Java 2023年5月26日
    00
  • 什么是volatile关键字?

    什么是volatile关键字? volatile是C语言关键字之一,用于修饰变量。 通常情况下,当一个变量被定义后,系统在运行时会在内存中为其分配一块地址,该变量被存储在该内存地址中。当程序运行时会从该地址中读取该变量的值,不过在实际的程序中,可能会遇到一些特殊情况,这些特殊情况可能会导致该变量的值不再在该内存地址中,而是在其他位置上,这个时候就可以通过vo…

    Java 2023年5月10日
    00
  • java根据扩展名获取系统图标和文件图标示例

    1. 获取系统图标和文件图标的背景知识 在讲解获取系统图标和文件图标的方法之前,我们需要了解一些背景知识。 当我们在操作系统中打开一个文件夹或文件时,会显示相应的图标。这些图标保存在操作系统的系统图标库中,即以 .dll 文件形式存在的文件,比如 Windows 中的 shell32.dll 文件。 Java 提供了获取系统图标和文件图标的方法,它们都是通过…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部