当前位置: 首页 > news >正文

Java并发容器和原子类

1. Java并发容器

1.1. 并发容器概览

  • Java 并发容器是 Java 并发包(java.util.concurrent)的重要组成,1.5 后提供了丰富的线程安全容器。
  • 分类仍为四大类:
    • List
    • Map
    • Set
    • Queue

1.2. 同步容器(synchronized-based)

示例:

List list = Collections.synchronizedList(new ArrayList<>());
Set set = Collections.synchronizedSet(new HashSet<>());
Map map = Collections.synchronizedMap(new HashMap<>());

特点:

  • 基于 synchronized 实现,保证线程安全。
  • 也包括早期的 Vector、Stack、Hashtable 等。

注意事项

1. 组合操作非原子,如:

if (!list.contains(x)) list.add(x); // 非原子!

➤ 推荐封装为原子方法并加锁,例如:

synchronized boolean addIfNotExist(T t) { ... }

2. 遍历时要手动加锁,否则存在并发修改问题:

synchronized (list) {Iterator i = list.iterator();while (i.hasNext()) foo(i.next());
}

1.3. 并发容器(concurrent-based)

优势:

  • 使用非阻塞算法、分段锁等机制,性能远优于同步容器。
  • 仍然是 List、Map、Set、Queue 四类,但实现更复杂更优化。

1. List

CopyOnWriteArrayList

  • 写时复制:每次写操作都会复制一份新数组,写完后替换旧引用。
  • 适用于读多写少,如监听器列表、缓存等。

使用注意

  • 写操作性能差(因为复制数组)。
  • 读操作可能看到旧数据(弱一致性)。
  • 其迭代器是只读快照不支持增删改

2. Map

ConcurrentHashMap

  • 高性能 Map,实现无锁或低锁分段操作。
  • key 和 value 不能为 null

ConcurrentSkipListMap

  • 基于跳表(SkipList),key 有序,可替代 TreeMap。
  • 插入/查询效率 O(log n),适用于高并发且需要排序的场景。

注意:

3. Set

  • 实现类:
    • CopyOnWriteArraySet:基于 CopyOnWriteArrayList
    • ConcurrentSkipListSet:基于 ConcurrentSkipListMap
  • 使用场景与对应的 List/Map 类似。

4. Queue(队列,最复杂)

维度一:是否阻塞

  • 阻塞队列:BlockingQueue 系列
  • 非阻塞队列:ConcurrentLinkedQueue

维度二:是否双端

  • 单端:只能队尾入队、队首出队
  • 双端:两端都可入队出队(Deque

常见实现对照表:

分类

实现类

特点/用途

单端阻塞队列

ArrayBlockingQueue, LinkedBlockingQueue

有界,推荐使用

SynchronousQueue

无缓冲,生产者必须等消费者

PriorityBlockingQueue

带优先级的阻塞队列

DelayQueue

可延时出队

LinkedTransferQueue

混合阻塞/直接传递,性能优

双端阻塞队列

LinkedBlockingDeque

支持两端操作

单端非阻塞队列

ConcurrentLinkedQueue

无界,高并发性能优

双端非阻塞队列

ConcurrentLinkedDeque

双端无锁队列

注意:

  • 无界队列存在 OOM 风险,除 ArrayBlockingQueueLinkedBlockingQueue 其他均为无界。
  • 实际应用中建议优先选择有界队列

1.4. 总结与建议

选择容器的建议:

需求

推荐容器

多线程读多写少

CopyOnWriteArrayList

高并发无序 Map

ConcurrentHashMap

高并发有序 Map

ConcurrentSkipListMap

高并发队列,双端支持

ConcurrentLinkedDeque

阻塞队列,生产者-消费者模型

LinkedBlockingQueueArrayBlockingQueue

阻塞队列且需优先级或延迟

PriorityBlockingQueue / DelayQueue

常见“坑”:

  • 组合操作不是原子(需手动同步)
  • 同步容器遍历时需加锁
  • CopyOnWrite 容器写入延迟,迭代器只读
  • ConcurrentMap 的 key/value 不能为 null
  • 无界队列可能 OOM

同步容器VS并发容器:

特性

同步容器(Synchronized)

并发容器(Concurrent)

引入版本

JDK 1.2(如 Collections.synchronizedXXX

JDK 1.5 及以后

线程安全实现方式

所有方法使用 synchronized实现同步

细粒度锁、CAS 等高效机制

性能

较差(所有操作都串行)

更高性能(部分操作并发执行)

适用场景

线程数量较少,竞争不激烈

高并发场景,要求更高性能

组合操作(如遍历+修改)

手动加锁(如 synchronized(list)

内部支持更强的并发控制

常见类

Vector, Hashtable, Collections.synchronizedList

ConcurrentHashMap

, CopyOnWriteArrayList

, ConcurrentLinkedQueue

2. 原子类:无锁工具的典范

2.1. 为什么需要原子类?

count += 1 为例,它不是原子操作,因为涉及三个步骤:

  1. 从主内存读取 count;
  2. 执行加一操作;
  3. 写回主内存。

多个线程同时执行会出现线程安全问题。使用互斥锁可以解决,但代价较高(加锁/解锁+线程切换)。原子类就是为了解决这个问题的无锁方案

AtomicLong 示例:如何用原子类解决问题?(无锁方案)

public class Test {AtomicLong count = new AtomicLong(0);void add10K() {int idx = 0;while(idx++ < 10000) {count.getAndIncrement();}}
}
  • count.getAndIncrement():原子操作,无需加锁,即可线程安全地自增。

2.2. 无锁方案的实现原理(使用原子类)

无锁的定义:

无锁编程是一种在并发程序中避免使用互斥锁(mutex)的并发控制方式。其目标是让线程在不阻塞的情况下共享数据,避免死锁、线程切换带来的开销等问题。

核心原理:CAS(Compare And Swap)

无锁算法的基础是CAS 指令,即“比较并交换”:

boolean CAS(V expectedValue, V newValue)
  • 作为一条 CPU 指令,CAS 指令本身是能够保证原子性的。

执行逻辑:

  • 比较当前内存中的值是否等于 expectedValue
  • 如果相等,则用 newValue 更新该值,并返回 true
  • 如果不相等,说明被其他线程修改了,不做更新,返回 false

举例:自旋重试实现无锁自增

while (true) {int current = atomicValue.get();int next = current + 1;if (atomicValue.compareAndSet(current, next)) {break;}// 如果 CAS 失败则自旋重试
}

无锁的优势:

  • 不阻塞,不会引起线程上下文切换
  • 并发性能好,尤其在高并发下

无锁的挑战:

  • 编码复杂
  • 容易发生 ABA 问题(即 A → B → A,CAS 检查不出变更)
  • 不适合临界区过大或逻辑复杂的场景

CAS 的经典问题:ABA 问题

什么是 ABA 问题?

  • 假设线程 T1 读取 count 为 A;
  • 线程 T2 把 count 改为 B;
  • 线程 T3 又把 count 改回 A;
  • T1 再次用 CAS 判断:值等于 A,CAS 成功;
  • 但实际上,count 已经被修改过,这就是 ABA 问题。

如何解决?

通过引入版本号或标记:

  • AtomicStampedReference:使用整型版本号;
  • AtomicMarkableReference:使用 boolean 标记。

2.3. 原子类的概念

什么是原子类(Atomic Class)?

原子类是 Java 并发包 java.util.concurrent.atomic 中提供的一组类,用于实现原子性操作的共享变量。常见如:

原子类

说明

AtomicInteger

原子性的 int 类型变量

AtomicLong

原子性的 long 类型变量

AtomicReference<T>

原子引用类型变量

AtomicBoolean

原子布尔类型变量

  • 所有操作都是线程安全的
  • 不依赖显式加锁(如 synchronized
  • 底层使用 CAS 保证原子性

2.4. 各语言原子操作示例

1. Python 原子操作示例

Python 的标准库中并没有直接的原子类(如 Java、Go),但可以借助 threading.Lockmultiprocessing.Valueatomicwrites,或使用第三方库 atomics 来实现。

示例(使用 threading.Lock 模拟原子操作):

import threadingclass AtomicInteger:def __init__(self, initial=0):self.value = initialself._lock = threading.Lock()def increment(self):with self._lock:self.value += 1return self.value# 测试
counter = AtomicInteger()def task():for _ in range(10000):counter.increment()threads = [threading.Thread(target=task) for _ in range(10)]for t in threads:t.start()
for t in threads:t.join()print("Final value:", counter.value)
  • Python 的 GIL 限制了真正的并行性,但锁还是必需的,因为 I/O 或 C 扩展可能释放 GIL。

2. Java 原子操作示例

Java 原子类由 java.util.concurrent.atomic 包提供,最常用的是 AtomicInteger

示例:

import java.util.concurrent.atomic.AtomicInteger;public class AtomicExample {private static AtomicInteger counter = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Runnable task = () -> {for (int i = 0; i < 10000; i++) {counter.incrementAndGet();  // 原子递增}};Thread[] threads = new Thread[10];for (int i = 0; i < 10; i++) {threads[i] = new Thread(task);threads[i].start();}for (Thread t : threads) {t.join();}System.out.println("Final value: " + counter.get());}
}
  • incrementAndGet() 方法底层使用 CAS 实现原子性。
  1. Go(Golang)原子操作示例

Go 提供了 sync/atomic 包用于原子操作。

示例:

package mainimport ("fmt""sync""sync/atomic"
)func main() {var counter int64 = 0var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func() {for j := 0; j < 10000; j++ {atomic.AddInt64(&counter, 1) // 原子加法}wg.Done()}()}wg.Wait()fmt.Println("Final value:", counter)
}
  • atomic.AddInt64 是线程安全的,不需要额外加锁。

总结比较:

特性

Python

Java

Go

原子操作支持

❌(需自定义或第三方)

AtomicInteger

sync/atomic

原子加操作

自定义加锁实现

incrementAndGet()

atomic.AddInt64()

底层机制

依赖 GIL + Lock

CAS + volatile + Unsafe

CAS(基于硬件指令)

相关文章:

  • caliper中的测试文件写法及其注意事项
  • 谷歌云代理商 | 游戏行业专属方案:谷歌云实时多人游戏服务器架构
  • 在Windows下利用LoongArch-toolchain交叉编译Qt
  • C++编程——关于比较器的使用
  • 五子棋网络对战游戏的设计与实现设计与实现【源码+文档】
  • 常见工具导出DDL语句
  • 图片切割工具:智能分割长图并控制文件大小
  • 三维GIS开发cesium智慧地铁教程(4)城市白模加载与样式控制
  • 数据任务调度解决离不开离线开发BatchWorks
  • 单周期cpu和多周期cpu、单周期数据通路和多周期数据通路与总线结构数据通路和专用数据通路的关系
  • AOP实现Restful接口操作日志入表方案
  • CC7利用链深度解析
  • 基于3D对象体积与直径特征的筛选
  • 【Linux】find 命令详解及使用示例:递归查找文件和目录
  • EtherNet/IP转DeviceNet协议网关详解
  • C++.OpenGL (9/64)摄像机(Camera)
  • mysql的分页场景下,页数越大查询速度越慢的解决方法
  • 3D Web轻量化引擎HOOPS Communicator的定制化能力全面解析
  • java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟
  • 11.RV1126-ROCKX项目
  • 武汉公司做网站/竞价代运营
  • java 网站开发 顺序/免费收录平台
  • wordpress 宠物/seo关键词
  • 湖州网站建设湖州网站建设/成crm软件
  • 佛山企业网站seo/百度推广怎么联系
  • 企业网站建设套餐/最近营销热点