Java高并发编程(2)
以下是针对并发编程中线程安全 List、ConcurrentLinkedQueue、BlockingQueue 及跳表的详细解析,结合原理、使用场景和代码示例进行说明:
一、线程安全 List 的实现类及使用场景
-
CopyOnWriteArrayList
- 原理:采用写时复制(Copy-On-Write),每次修改操作(如
add
、set
)会复制底层数组,在新数组上操作,避免读写冲突。 - 适用场景:读多写极少(如缓存、配置管理)。
- 优点:读操作无锁,迭代器弱一致性(不抛
ConcurrentModificationException
)。 - 缺点:写操作昂贵(复制整个数组),内存占用高。
- 示例:
List<String> list = new CopyOnWriteArrayList<>(); list.add("A"); // 写时复制 String item = list.get(0); // 读操作无锁
- 原理:采用写时复制(Copy-On-Write),每次修改操作(如
-
Collections.synchronizedList()
- 原理:通过
synchronized
关键字对所有方法加锁(如get
、add
)。 - 适用场景:低并发写入场景。
- 缺点:锁粒度大,高并发时性能差;迭代时需手动加锁避免
ConcurrentModificationException
。 - 示例:
List<String> syncList = Collections.synchronizedList(new ArrayList<>()); synchronized(syncList) { // 迭代时手动加锁for (String s : syncList) { ... } }
- 原理:通过
-
其他线程安全方案
Vector
:历史遗留类,所有方法用synchronized
修饰,性能较差,不推荐使用。ConcurrentHashMap + List
:按 Key 分区存储,减少锁竞争,适合大数据量分类存储。
二、ConcurrentLinkedQueue
(无锁并发队列)
核心特性
- 非阻塞无锁设计:基于 CAS(Compare-And-Swap)实现线程安全,无锁竞争。
- 无界队列:动态扩展链表节点,理论容量无限(受限于内存)。
- FIFO 顺序:严格保证先进先出。
使用场景
- 高并发写入:如日志收集、事件总线(生产者远多于消费者)。
- 非阻塞任务队列:需要高吞吐且无需等待的场景。
常用方法
方法 | 作用 | 返回值 |
---|---|---|
offer(e) | 插入元素(尾部) | true (总是成功) |
poll() | 移除并返回头部元素(队列空则返回 null ) | 元素或 null |
peek() | 查看头部元素(不移除) | 元素或 null |
isEmpty() | 判断队列是否为空 | boolean |
size() | O(n) 时间复杂度,慎用! | 队列元素数量 |
代码示例
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
// 生产者线程
queue.offer("Task1");
// 消费者线程
String task = queue.poll();
if (task != null) {System.out.println("Processed: " + task);
}
注意:size()
方法需遍历链表,性能差,避免高频调用。
三、BlockingQueue
(阻塞队列)
核心特性
- 阻塞操作:
- 队列空时,消费者调用
take()
阻塞等待; - 队列满时,生产者调用
put()
阻塞等待。
- 队列空时,消费者调用
- 有界/无界:
ArrayBlockingQueue
固定大小,LinkedBlockingQueue
可选有界(默认Integer.MAX_VALUE
)。 - 线程安全:内部通过
ReentrantLock
实现同步。
使用场景
- 生产者-消费者模式:如线程池任务队列(
ThreadPoolExecutor
默认使用LinkedBlockingQueue
)。 - 流量控制:通过有界队列限制系统负载(如防止内存溢出)。
常用实现类
实现类 | 特点 |
---|---|
ArrayBlockingQueue | 数组实现,固定容量,公平锁可选 |
LinkedBlockingQueue | 链表实现,可选容量,吞吐量高 |
Prio |