Java 中的 ArrayBlockingQueue
ArrayBlockingQueue是 Java 中java.util.concurrent包下的一个线程安全的阻塞队列实现。它是基于数组的有界队列,支持先进先出(FIFO)的顺序。ArrayBlockingQueue是一个非常常用的工具,特别是在多线程环境中,用于线程之间的通信和任务调度。
主要特点
线程安全
:ArrayBlockingQueue内部使用了锁机制来保证线程安全。
有界队列
:队列的容量在创建时必须指定,不能动态扩展。
FIFO 顺序
:元素按照先进先出的顺序进行插入和移除。
阻塞操作
:支持阻塞的put和take操作,当队列满或空时,线程会阻塞直到可以继续操作。
可选公平性
:可以通过构造函数指定是否使用公平锁。公平性会影响性能,通常非公平锁性能更高。
构造方法
ArrayBlockingQueue提供了以下构造方法:
// 创建一个指定容量的队列,默认非公平锁
ArrayBlockingQueue(int capacity)
// 创建一个指定容量的队列,并指定是否使用公平锁
ArrayBlockingQueue(int capacity, boolean fair)
// 创建一个指定容量的队列,并用已有集合初始化队列内容
ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c)
capacity
:队列的容量,必须大于 0。
fair
:是否使用公平锁,true表示公平,false表示非公平。
常用方法
以下是ArrayBlockingQueue的常用方法:
插入操作
boolean add(E e)
:将元素插入队列尾部,如果队列已满,抛出IllegalStateException。
boolean offer(E e)
:将元素插入队列尾部,如果队列已满,返回false。
void put(E e)
:将元素插入队列尾部,如果队列已满,阻塞直到有空间。
移除操作
E remove()
:移除并返回队列头部的元素,如果队列为空,抛出NoSuchElementException。
E poll()
:移除并返回队列头部的元素,如果队列为空,返回null。
E take()
:移除并返回队列头部的元素,如果队列为空,阻塞直到有元素。
检查操作
E peek()
:返回队列头部的元素,但不移除。如果队列为空,返回null。
int size()
:返回队列中当前元素的数量。
boolean isEmpty()
:检查队列是否为空。
boolean contains(Object o)
:检查队列是否包含指定元素。
代码示例
以下是一个使用ArrayBlockingQueue的简单示例:
import java.util.concurrent.ArrayBlockingQueue;
public class ArrayBlockingQueueExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个容量为 3 的 ArrayBlockingQueue
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
// 插入元素
queue.put("A");
queue.put("B");
queue.put("C");
// 尝试插入第四个元素(会阻塞,因为队列已满)
new Thread(() -> {
try {
System.out.println("Trying to add D...");
queue.put("D");
System.out.println("Added D");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 模拟延迟 2 秒后移除一个元素
Thread.sleep(2000);
System.out.println("Removed: " + queue.take());
// 此时队列有空间,线程可以成功插入 D
}
}
输出示例:
Trying to add D...
Removed: A
Added D
公平锁与非公平锁的区别
公平锁
:线程按照请求的顺序获取锁,避免线程饥饿。
非公平锁
:线程可以“插队”获取锁,可能导致某些线程长时间等待,但性能通常更高。
在ArrayBlockingQueue中,默认使用非公平锁。如果需要公平性,可以在构造时设置fair参数为true。
注意事项
容量限制
:ArrayBlockingQueue是有界队列,容量必须在创建时指定,且不能动态扩展。如果需要动态扩展的队列,可以使用LinkedBlockingQueue。
性能权衡
:公平锁虽然避免了线程饥饿,但会增加线程切换的开销,降低性能。
阻塞操作
:put和take是阻塞操作,使用时需要注意避免死锁。
适用场景
生产者-消费者模型
:ArrayBlockingQueue是实现生产者-消费者模式的理想选择。
任务调度
:可以用作任务队列,多个线程从队列中取任务并执行。
线程间通信
:在多线程环境中,用于线程之间的数据传递。
总结
ArrayBlockingQueue是一个功能强大且易用的阻塞队列,适合在多线程环境中使用。它的有界特性和线程安全性使其成为实现高效任务调度和线程间通信的理想选择。在使用时,需要根据具体场景选择公平锁或非公平锁,并注意容量限制对程序的影响。
领取专属 10元无门槛券
私享最新 技术干货