Java并发编程中,当多个线程同时操作同一个容器时,普通的ArrayList、HashMap等容器没有做线程安全处理,很容易出现数据覆盖、并发修改异常等问题,并发容器就是为了解决这类多线程场景下的容器操作安全问题而设计的特殊容器,它在内部通过锁分段、CAS无锁算法、写时复制等机制,保证多线程操作时的线程安全,同时尽可能减少性能损耗。

什么是并发容器
并发容器是Java util.concurrent包下提供的,支持多线程并发访问的容器实现,和普通容器的核心区别是它内置了线程安全机制,不需要开发者额外通过synchronized等关键字加锁就能在多线程场景下安全使用。和早期通过Collections工具类的synchronizedMap、synchronizedList包装出来的同步容器不同,并发容器的锁粒度更细,性能通常更好,比如同步容器是对整个容器加锁,而ConcurrentHashMap在JDK8之后采用CAS加synchronized的方式,只锁当前操作的节点,并发度更高。
常见并发容器及使用说明
CopyOnWriteArrayList
CopyOnWriteArrayList是基于写时复制机制实现的并发List容器,适合读多写少的场景。它的原理是当执行修改操作(新增、删除、修改)时,会先复制一份当前数组的副本,在副本上完成修改,再将容器的数组引用指向新的副本,而读操作直接读当前数组,不需要加锁。这种机制保证了读操作完全无锁,性能很高,但写操作因为要复制数组,开销比较大,所以如果写操作频繁,不适合使用这个容器。
使用示例:
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListDemo {
public static void main(String[] args) {
// 创建CopyOnWriteArrayList实例
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 新增元素
list.add("Java");
list.add("并发编程");
// 读取元素
System.out.println(list.get(0)); // 输出 Java
// 遍历元素,遍历过程中即使其他线程修改容器,也不会抛出并发修改异常
for (String item : list) {
System.out.println(item);
}
// 删除元素
list.remove("并发编程");
System.out.println(list.size()); // 输出 1
}
}
ConcurrentHashMap
ConcurrentHashMap是并发场景下常用的Map容器,替代了同步的HashMap,性能远高于Collections.synchronizedMap包装的HashMap。JDK8之后的ConcurrentHashMap放弃了之前的锁分段机制,采用数组+链表+红黑树的结构,利用CAS操作和synchronized对当前链表头节点或者红黑树根节点加锁,锁粒度更细,并发度更高。它支持高并发的读和写操作,并且提供了一些原子性的操作方法,比如putIfAbsent、computeIfAbsent等。
使用示例:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapDemo {
public static void main(String[] args) {
// 创建ConcurrentHashMap实例
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 新增键值对
map.put("a", 1);
map.put("b", 2);
// 原子性操作:如果键不存在则插入
map.putIfAbsent("a", 3);
System.out.println(map.get("a")); // 输出 1,因为a已经存在,不会覆盖
// 原子性操作:如果键存在则计算新值
map.computeIfPresent("b", (key, value) -> value + 1);
System.out.println(map.get("b")); // 输出 3
// 遍历键值对
map.forEach((key, value) -> System.out.println(key + ":" + value));
}
}
ConcurrentLinkedQueue
ConcurrentLinkedQueue是一个基于链表结构的无锁并发队列,采用CAS算法保证线程安全,适合高并发场景下的队列操作。它不允许插入null元素,性能和线程安全都优于通过synchronized包装的队列,通常用于生产者消费者场景中的消息传递。
使用示例:
import java.util.concurrent.ConcurrentLinkedQueue;
public class ConcurrentLinkedQueueDemo {
public static void main(String[] args) {
// 创建ConcurrentLinkedQueue实例
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
// 入队操作
queue.offer("消息1");
queue.offer("消息2");
// 出队操作,poll方法取出并删除队首元素,队列为空时返回null
String msg1 = queue.poll();
System.out.println(msg1); // 输出 消息1
// peek方法取出但不删除队首元素,队列为空时返回null
String msg2 = queue.peek();
System.out.println(msg2); // 输出 消息2
}
}
BlockingQueue系列容器
BlockingQueue是阻塞队列的父接口,常见的实现类有ArrayBlockingQueue、LinkedBlockingQueue等,它除了支持普通的队列操作,还提供了阻塞的入队和出队方法:当队列满时,put方法会阻塞直到队列有空位;当队列空时,take方法会阻塞直到队列有元素。这个特性让它非常适合生产者消费者模式,不需要开发者额外控制阻塞逻辑。
ArrayBlockingQueue使用示例:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
// 创建容量为2的ArrayBlockingQueue
BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
// 入队,队列满时put方法会阻塞
queue.put("任务1");
queue.put("任务2");
// 队列已满,下面这行代码会阻塞直到有元素被取出
// queue.put("任务3");
// 出队,队列空时take方法会阻塞
String task1 = queue.take();
System.out.println(task1); // 输出 任务1
}
}
并发容器选择建议
在实际开发中选择并发容器可以参考以下原则:如果是List场景,读多写少选CopyOnWriteArrayList,写多的话可以考虑其他方案或者评估是否适合用这个容器;如果是Map场景,优先选ConcurrentHashMap;如果是队列场景,需要阻塞特性选BlockingQueue系列,不需要阻塞选ConcurrentLinkedQueue。同时要注意不同并发容器的特性,比如CopyOnWriteArrayList的写操作开销大,ConcurrentHashMap不支持null键值等,避免因为特性不了解导致使用错误。
Java并发编程并发容器CopyOnWriteArrayListConcurrentHashMap修改时间:2026-06-19 15:09:20