马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
在并发编程中,线程安全的队列是办理线程间任务转达和调治的关键工具之一。壅闭队列(BlockingQueue)作为一种线程安全的队列,实现了在并发环境下对共享数据的安全访问,广泛应用于生产者-斲丧者模子、任务调治和多线程盘算中。本文将具体先容壅闭队列的概念、常见实现、线程安全原理及与线程池的团结使用,资助你全面把握 Java 中壅闭队列的应用。
1. 什么是壅闭队列
壅闭队列(BlockingQueue)是一个线程安全的队列,它支持在特定条件下对队列的利用举行壅闭。BlockingQueue 接口继续自 Queue,并提供了几个焦点方法:take()、put()、offer() 和 poll(),此中 take() 和 put() 是壅闭利用,可以大概在队列为空时等候数据,或在队列满时等候空闲空间。- public interface BlockingQueue<E> extends Queue<E> {
- void put(E e) throws InterruptedException;
- E take() throws InterruptedException;
- boolean offer(E e);
- E poll();
- // 其他方法
- }
复制代码 应用场景:
- 生产者斲丧者模子:多个生产者线程将任务放入队列,多个斲丧者线程从队列中取出任务实行,队列的巨细决定了体系的缓冲本领。
- 任务调治:队列可以用来调治和管理任务,包管任务的次序实行和线程间的调和。
2. 紧张的并发队列关系图
Java 提供了多种线程安全的队列,紧张可以分为两类:
- 壅闭队列(BlockingQueue)
- 非壅闭队列(如 ConcurrentLinkedQueue)
这两类队列各自实用于差别的场景,壅闭队列得当于须要控制线程协作的场景,非壅闭队列则得当于高并发、高性能的无壅闭任务处理惩罚。
3. 壅闭队列的特点
壅闭队列的最大特点是它的壅闭利用,紧张体现在以下两个方法:
- take():如果队列为空,斲丧者线程会被壅闭,直到队列中有数据可用。
- put():如果队列已满,生产者线程会被壅闭,直到队列有空闲空间。
这些方法的壅闭特性使得壅闭队列非常得当于生产者-斲丧者模子,它可以大概包管任务的有序实行,而且自动控制线程的实行次序。
4. 常用方法
常见的 BlockingQueue 方法包罗:
- add():向队列中添加元素,队列已满时抛出非常。
- remove():移除并返回队列头部的元素,队列为空时抛出非常。
- offer():向队列中添加元素,队列已满时返回 false。
- poll():移除并返回队列头部的元素,队列为空时返回 null。
- put():向队列中添加元素,队列已满时壅闭当火线程,直到有空间可用。
- take():从队列中获取并移除元素,队列为空时壅闭当火线程,直到有数据可用。
5. 常见壅闭队列
Java 提供了多种实现了 BlockingQueue 接口的常见壅闭队列,每种队列的实现都具有差别的特点,实用于差别的应用场景。
5.1 ArrayBlockingQueue
ArrayBlockingQueue 是一个有界壅闭队列,内部使用数组存储元素,具有固定的容量,实用于任务数已知且较为稳固的场景。- BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
复制代码 5.2 LinkedBlockingQueue
LinkedBlockingQueue 是一个基于链表的壅闭队列,可以设置容量,容量默认值为 Integer.MAX_VALUE。实用于任务量动态变革的场景。- BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(100);
复制代码 5.3 SynchronousQueue
SynchronousQueue 是一种特殊的壅闭队列,其容量为 0。每次生产任务时,必须有斲丧者线程来吸取该任务,否则生产者会被壅闭。实用于快速转达任务的场景。- BlockingQueue<Integer> queue = new SynchronousQueue<>();
复制代码 5.4 PriorityBlockingQueue
PriorityBlockingQueue 是一个无界的壅闭队列,支持优先级排序,队列中的元素根据优先级举行排序,实用于须要处理惩罚优先级任务的场景。- BlockingQueue<Integer> queue = new PriorityBlockingQueue<>();
复制代码 5.5 DelayQueue
DelayQueue 是一个支持延时任务的无界壅闭队列,任务可以设置延长时间,任务到期后才会被斲丧。实用于定时任务调治的场景。- BlockingQueue<Delayed> queue = new DelayQueue<>();
复制代码 6. 壅闭和非壅闭队列的并发安全原理
6.1 ArrayBlockingQueue 源码分析
ArrayBlockingQueue 内部使用数组存储元素,使用 ReentrantLock 和 Condition 实现并发控制。put() 和 take() 方法会通过 lock 锁住队列,壅闭利用使用 notFull 和 notEmpty 条件变量来控制线程的同步。- public void put(E e) throws InterruptedException {
- lock.lockInterruptibly();
- try {
- while (count == items.length)
- notFull.await();
- enqueue(e);
- } finally {
- lock.unlock();
- }
- }
复制代码
- ReentrantLock:提供了对队列的独占锁,确保线程在利用队列时是互斥的。
- Condition:通过 notFull 和 notEmpty 条件变量来控制线程的等候和叫醒。
6.2 非壅闭队列 ConcurrentLinkedQueue
ConcurrentLinkedQueue 是一个无界的非壅闭队列,内部通过 CAS(Compare-And-Swap)机制实现线程安全,实用于高并发场景。它使用 compareAndSwapObject 方法举行原子利用,包管多个线程同时访问队列时不发生辩说。- public boolean offer(E e) {
- final Node<E> newNode = new Node<>(e);
- for (Node<E> t = tail, p = t;;) {
- Node<E> q = p.next;
- if (q == null) {
- if (p.casNext(null, newNode)) {
- if (p != t)
- casTail(t, newNode);
- return true;
- }
- }
- p = q;
- }
- }
复制代码 7. 线程池与壅闭队列
线程池与壅闭队列经常一起使用,壅闭队列作为线程池的任务队列,用于存储待处理惩罚的任务。常见的线程池范例及其与壅闭队列的共同关系如下:
线程池范例壅闭队列范例FixedThreadPoolLinkedBlockingQueueSingleThreadExecutorLinkedBlockingQueueCachedThreadPoolSynchronousQueueScheduledThreadPoolDelayWorkQueueSingleThreadScheduledExecutorDelayedWorkQueue7.1 LinkedBlockingQueue
实用于 FixedThreadPool 和 SingleThreadExecutor,由于这两个线程池的线程数固定,任务队列的容量可以设置较大,确保不会由于队列满而拒绝任务。
7.2 SynchronousQueue
实用于 CachedThreadPool,它的容量为 0,每个任务都会立刻被实行,因此线程池的线程数可以动态变革。
7.3 DelayWorkQueue
实用于定时任务,如 ScheduledThreadPool 和 SingleThreadScheduledExecutor,可以大概根据任务的延长时间举行调治。
总结
壅闭队列是并发编程中的一个紧张工具,它通过线程安全的队列机制,包管了在多线程环境下的数据转达和调和。Java 提供了多种实现方式,如 ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue 等,可以根据差别的业务需求选择符合的壅闭队列范例。把握壅闭队列的使用和原理,可以大概资助你构建更加高效和可靠的并发步伐。
🌟 关注我的CSDN博客,劳绩更多技能干货! 🌟
|