Java阻塞队列是Java多线程并发编程的重要功能,下面就全面来详解Java阻塞队列的特点以及种类@mikechen
Java阻塞队列定义
Java中的阻塞队列,全称是Java Blocking Queue,是一种线程安全的队列实现,它提供了在队列为空或已满时进行阻塞等待的特性。
举个例子来说明一下吧:我们去餐馆吃饭,万一这家店生意好餐馆挤满了人,这时候肯定不能把顾客赶出去,于是餐馆就在旁边设置了一个休息等待区,这就是一个阻塞队列了。
Java阻塞队列原理
Java阻塞队列的实现原理,主要包含如下5大部分:
1.底层数据结构
阻塞队列的底层通常采用数组或链表等数据结构。
比如:ArrayBlockingQueue
使用数组,而LinkedBlockingQueue
使用链表。
2.锁与条件变量
阻塞队列的实现通常使用锁和条件变量来控制并发访问,锁用于确保在对队列进行操作时的线程安全性,条件变量用于在队列为空或已满时进行阻塞和唤醒。
3.put 和 take 操作
阻塞队列的put
操作(将元素放入队列)和take
操作(从队列中取出元素)是核心操作。
当队列满或空时,对应的操作会阻塞等待,直到满足条件。
4.阻塞和唤醒
在队列为空或已满时,线程会被阻塞。
5.CAS(Compare and Swap)操作
一些实现中可能使用了CAS操作来进行非阻塞的并发控制,从而提高性能,这通常用于无界队列。
Java阻塞队列有哪些
在Java中,主要的阻塞队列实现类包括:
1.ArrayBlockingQueue
这是一个基于数组实现的有界阻塞队列,需要指定队列的容量。
如下所示:
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class ArrayBlockingQueueExample { public static void main(String[] args) throws InterruptedException { BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5); // 创建容量为5的有界阻塞队列 // 生产者线程 Thread producer = new Thread(() -> { try { for (int i = 0; i < 10; i++) { queue.put(i); // 将元素放入队列,如果队列已满会阻塞等待 System.out.println("Produced: " + i); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); // 消费者线程 Thread consumer = new Thread(() -> { try { for (int i = 0; i < 10; i++) { int value = queue.take(); // 从队列中取出元素,如果队列为空会阻塞等待 System.out.println("Consumed: " + value); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); producer.start(); consumer.start(); producer.join(); consumer.join(); } }
当队列已满时,生产者线程将会阻塞直到有空间可用,当队列为空时,消费者线程将会阻塞直到有元素可消费。
2.LinkedBlockingQueue
基于链表实现的可选有界或无界阻塞队列。
如下所示:
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class LinkedBlockingQueueExample { public static void main(String[] args) throws InterruptedException { BlockingQueue<String> queue = new LinkedBlockingQueue<>(); // 创建无界阻塞队列 // 生产者线程 Thread producer = new Thread(() -> { try { for (int i = 0; i < 5; i++) { queue.put("Item " + i); // 放入元素,无界队列不会阻塞 System.out.println("Produced: Item " + i); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); // 消费者线程 Thread consumer = new Thread(() -> { try { for (int i = 0; i < 5; i++) { String item = queue.take(); // 取出元素,如果队列为空会阻塞等待 System.out.println("Consumed: " + item); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); producer.start(); consumer.start(); producer.join(); consumer.join(); } }
如果初始化时指定容量,则是有界的。如果没有指定容量,则默认为无界队列。
3.PriorityBlockingQueue
这是一个基于优先级堆实现的无界阻塞队列,元素根据优先级顺序进行排列
如下所示:
import java.util.concurrent.BlockingQueue; import java.util.concurrent.PriorityBlockingQueue; public class PriorityBlockingQueueExample { public static void main(String[] args) throws InterruptedException { BlockingQueue<Integer> queue = new PriorityBlockingQueue<>(); // 创建无界优先级阻塞队列 // 生产者线程 Thread producer = new Thread(() -> { try { for (int i = 0; i < 10; i++) { queue.put(i); // 放入元素,会根据优先级进行排序 System.out.println("Produced: " + i); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); // 消费者线程 Thread consumer = new Thread(() -> { try { for (int i = 0; i < 10; i++) { int value = queue.take(); // 取出元素,如果队列为空会阻塞等待 System.out.println("Consumed: " + value); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); producer.start(); consumer.start(); producer.join(); consumer.join(); } }
4.DelayQueue
支持延迟操作的阻塞队列,用于定时任务调度。
5.SynchronousQueue
容量为1的阻塞队列,用于直接传递元素。
当生产者线程放入一个元素后,它会等待消费者线程将元素取走,反之亦然。
6.LinkedTransferQueue
无界阻塞队列,支持异步传输。
它支持异步传输,即在元素未被消费之前,生产者和消费者线程可以异步执行。
Java阻塞队列使用场景
- 生产者-消费者模式: 阻塞队列非常适合实现生产者-消费者模式,其中生产者线程将数据放入队列,而消费者线程从队列中取出数据,以实现线程之间的解耦。
- 线程池: 阻塞队列常用于线程池的任务队列,用于存放等待执行的任务,保证线程池中的线程不会无限制地增长,同时控制任务的执行速度。
- 定时任务调度: DelayQueue可以用于实现定时任务调度,其中任务将在指定的延迟时间之后被执行。
- 多线程协调: 在多线程协调和通信的场景中,阻塞队列可以作为中介,帮助线程之间进行数据交换和通信。
- 数据缓冲: 阻塞队列可以用于实现数据缓冲,让生产者和消费者之间的速度差异得到平衡。
总之,Java阻塞队列在需要协调多线程工作、平衡资源利用、控制并发访问等方面都发挥着重要的作用。