Java 提供了多种线程安全的集合,它们位于 java.util.concurrent
包下,主要分为以下几类:
1. 阻塞队列 (BlockingQueue)
阻塞队列是一种特殊的队列,当队列为空时,获取元素的操作会被阻塞,直到队列中有元素可用;当队列已满时,添加元素的操作会被阻塞,直到队列中有空闲位置。
ArrayBlockingQueue
: 基于数组的有界阻塞队列,按照 FIFO(先进先出)原则对元素进行排序。LinkedBlockingQueue
: 基于链表的可选有界阻塞队列(默认无界),按照 FIFO 原则对元素进行排序。PriorityBlockingQueue
: 无界阻塞队列,支持优先级排序。SynchronousQueue
: 同步队列,不存储元素,每个插入操作必须等待另一个线程的移除操作,反之亦然。DelayQueue
: 无界阻塞队列,只有在延迟期满时才能从中提取元素。LinkedTransferQueue
: 一个由链表结构组成的无界阻塞队列,它在SynchronousQueue的基础上添加了transfer方法。
代码示例 (ArrayBlockingQueue):
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
// 生产者线程
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 20; i++) {
queue.put("Item " + i); // 如果队列满了,会阻塞
System.out.println("Produced: Item " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 20; i++) {
String item = queue.take(); // 如果队列为空,会阻塞
System.out.println("Consumed: " + item);
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
2. 并发 Map (ConcurrentMap)
ConcurrentMap
接口是 Map
接口的子接口,提供了线程安全的并发操作。
ConcurrentHashMap
: 基于哈希表的线程安全 Map 实现,使用分段锁(JDK 1.7)或 CAS + synchronized (JDK 1.8+) 来实现并发控制。ConcurrentSkipListMap
: 基于跳表 (Skip List) 的线程安全 Map 实现,支持排序。
代码示例 (ConcurrentHashMap):
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 多个线程并发地向 map 中添加元素
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
map.putIfAbsent("Key" + i, i); // 原子操作
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
}
}
3. 并发 Set (ConcurrentSet)
ConcurrentSkipListSet
: 基于跳表的线程安全的Set,支持排序。CopyOnWriteArraySet
: 基于CopyOnWriteArrayList
实现的线程安全 Set。
4. 并发 List (ConcurrentList)
CopyOnWriteArrayList
: 线程安全的 List 实现,通过 写时复制 (Copy-On-Write) 的方式来实现并发控制。每次修改操作都会创建一个新的数组副本,读操作在原来的数组上进行,因此读操作不需要加锁,可以并发进行。适用于读多写少的场景。
代码示例 (CopyOnWriteArrayList):
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
// 读线程
Runnable reader = () -> {
for (String item : list) {
System.out.println("Read: " + item);
}
};
// 写线程
Runnable writer = () -> {
list.add("New Item");
System.out.println("Added: New Item");
};
}
}
5. 其他
Collections.synchronizedXXX
方法:Collections
工具类提供了一些静态方法,可以将非线程安全的集合包装成线程安全的集合,例如:Collections.synchronizedList(List<T> list)
Collections.synchronizedSet(Set<T> s)
Collections.synchronizedMap(Map<K,V> m)
这些方法通过在每个方法上添加synchronized
关键字来实现线程安全,性能较低。
总结:
Java 并发包提供了丰富的线程安全集合,可以满足不同的并发编程需求。选择合适的线程安全集合需要考虑以下因素:
- 数据结构: 数组、链表、哈希表、跳表等。
- 是否有界: 有界、无界。
- 排序: 是否需要排序。
- 读写比例: 读多写少、写多读少、读写均衡。
- 性能要求: 对并发性能的要求。
问题分析:
这个问题考察了 Java 中线程安全集合的种类和用法。
与其他问题的知识点联系:
- Java 线程安全的集合有哪些? 这是对 Java 并发集合的整体了解。
- Java 中的 synchronized 是怎么实现的?
Collections.synchronizedXXX
方法使用了synchronized
关键字。 - Java 中 ReentrantLock 的实现原理是什么? 一些并发集合(如
ArrayBlockingQueue
)内部使用了ReentrantLock
。 - 什么是 Java 的 CAS(Compare-And-Swap)操作? 一些并发集合(如
ConcurrentHashMap
)使用了 CAS 操作。 - 你使用过 Java 中的哪些阻塞队列? 阻塞队列是线程安全集合的重要组成部分。
理解这些联系可以帮助你更全面地掌握 Java 并发编程的知识,并了解如何在实际应用中选择合适的线程安全集合。