
本文深入探讨了在Java中如何正确初始化一个包含指定数量、且内部元素为`AtomicInteger`的`BlockingQueue`列表,并确保该列表本身是线程安全的。文章阐明了`ArrayList`容量与实际元素数量的区别,以及`BlockingQueue`实例化时的容量要求,提供了基于Stream API和传统for循环的两种高效且线程安全的实现方案,旨在帮助开发者避免常见误区,构建健壮的并发数据结构。
在Java并发编程中,我们经常需要处理线程安全的数据结构。当需要一个包含多个并发队列的列表时,例如List<BlockingQueue<AtomicInteger>>,并希望这个列表本身也是线程安全的,开发者可能会遇到一些常见的误区。本教程将详细介绍如何正确地构建和初始化这样的数据结构。
一个常见的误解是,当使用new ArrayList<ArrayBlockingQueue<AtomicInteger>>(15)构造ArrayList时,会认为列表已经包含了15个元素。然而,ArrayList的构造函数中指定的容量参数(例如15)仅仅是为其内部数组分配了初始存储空间,以优化后续元素添加的性能,它并不会在列表中实际添加任何元素。因此,new ArrayList<>(15)创建的列表在初始化后,其size()方法返回的仍然是0。要向列表中添加元素,必须显式地调用add()方法。
BlockingQueue是一个接口,其具体实现如ArrayBlockingQueue在构造时通常需要指定其自身的容量。这个容量定义了队列可以存储的最大元素数量。例如,new ArrayBlockingQueue<>(capacity)会创建一个固定容量的阻塞队列。因此,在创建BlockingQueue实例并将其添加到列表中时,需要为每个队列指定合适的容量。
立即学习“Java免费学习笔记(深入)”;
为了得到一个线程安全的List,我们通常使用Collections.synchronizedList()方法对一个普通的List进行包装。这个方法会返回一个线程安全的列表视图,其所有操作都会通过内部锁进行同步。
以下是两种常用的方法来构建一个包含指定数量的BlockingQueue,并使其列表本身是同步的:
Java 8引入的Stream API提供了一种声明式且简洁的方式来生成和收集数据。结合Collectors.collectingAndThen(),我们可以在收集元素到列表之后,再对这个列表进行额外的转换(例如使其同步)。
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.Collections;
public class SynchronizedBlockingQueueList {
public static void main(String[] args) {
final int queueCapacity = 10; // 每个BlockingQueue的容量
final int numberOfQueues = 5; // 列表中BlockingQueue的数量
// 使用Stream API生成并同步列表
List<BlockingQueue<AtomicInteger>> listOfQueues =
Stream.generate(() -> new ArrayBlockingQueue<AtomicInteger>(queueCapacity))
.limit(numberOfQueues)
.collect(Collectors.collectingAndThen(
Collectors.toList(),
Collections::synchronizedList
));
System.out.println("使用Stream API创建的列表大小: " + listOfQueues.size());
// 验证每个队列的容量
listOfQueues.forEach(queue -> {
System.out.println(" 队列类型: " + queue.getClass().getSimpleName() + ", 容量: " + ((ArrayBlockingQueue)queue).remainingCapacity() + queue.size());
});
// 示例:向第一个队列添加元素
if (!listOfQueues.isEmpty()) {
listOfQueues.get(0).offer(new AtomicInteger(1));
System.out.println("第一个队列的元素数量: " + listOfQueues.get(0).size());
}
}
}代码解析:
对于不熟悉Stream API或偏好更直观的循环结构的开发者,传统的for循环也是一个清晰且有效的选择。
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class SynchronizedBlockingQueueListForLoop {
public static void main(String[] args) {
final int queueCapacity = 10; // 每个BlockingQueue的容量
final int numberOfQueues = 5; // 列表中BlockingQueue的数量
// 先创建一个普通的ArrayList
List<BlockingQueue<AtomicInteger>> tempListOfQueues = new ArrayList<>();
// 使用for循环手动添加BlockingQueue实例
for (int i = 0; i < numberOfQueues; i++) {
tempListOfQueues.add(new ArrayBlockingQueue<>(queueCapacity));
}
// 将普通的ArrayList包装成线程安全的List
List<BlockingQueue<AtomicInteger>> synchronizedListOfQueues =
Collections.synchronizedList(tempListOfQueues);
System.out.println("使用for循环创建的列表大小: " + synchronizedListOfQueues.size());
// 验证每个队列的容量
synchronizedListOfQueues.forEach(queue -> {
System.out.println(" 队列类型: " + queue.getClass().getSimpleName() + ", 容量: " + ((ArrayBlockingQueue)queue).remainingCapacity() + queue.size());
});
// 示例:向第二个队列添加元素
if (!synchronizedListOfQueues.isEmpty()) {
synchronizedListOfQueues.get(1).offer(new AtomicInteger(2));
System.out.println("第二个队列的元素数量: " + synchronizedListOfQueues.get(1).size());
}
}
}代码解析:
正确地初始化一个同步的BlockingQueue列表,关键在于理解ArrayList容量与大小的区别,以及BlockingQueue实例化时的容量要求。通过Stream API的collectingAndThen()方法或传统的for循环,我们都能有效地构建出符合需求的线程安全数据结构。选择哪种方法取决于个人偏好和项目代码风格。无论哪种方式,最终目标都是为了在并发环境中安全、高效地管理和操作这些队列。
以上就是在Java中构建同步的BlockingQueue列表的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号