首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 可见性、原子性和有序性问题:并发编程Bug的源头
02 | Java内存模型:看Java如何解决可见性和有序性问题
03 | 互斥锁(上):解决原子性问题
04 | 互斥锁(下):如何用一把锁保护多个资源?
05 | 一不小心就死锁了,怎么办?
06 | 用“等待-通知”机制优化循环等待
07 | 安全性、活跃性以及性能问题
08 | 管程:并发编程的万能钥匙
09 | Java线程(上):Java线程的生命周期
10 | Java线程(中):创建多少线程才是合适的?
11 | Java线程(下):为什么局部变量是线程安全的?
12 | 如何用面向对象思想写好并发程序?
13 | 理论基础模块热点问题答疑
14 | Lock和Condition(上):隐藏在并发包中的管程
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
16 | Semaphore:如何快速实现一个限流器?
17 | ReadWriteLock:如何快速实现一个完备的缓存?
18 | StampedLock:有没有比读写锁更快的锁?
19 | CountDownLatch和CyclicBarrier:如何让多线程步调一致?
20 | 并发容器:都有哪些“坑”需要我们填?
21 | 原子类:无锁工具类的典范
22 | Executor与线程池:如何创建正确的线程池?
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
24 | CompletableFuture:异步编程没那么难
25 | CompletionService:如何批量执行异步任务?
26 | Fork/Join:单机版的MapReduce
27 | 并发工具类模块热点问题答疑
28 | Immutability模式:如何利用不变性解决并发问题?
29 | Copy-on-Write模式:不是延时策略的COW
30 | 线程本地存储模式:没有共享,就没有伤害
31 | Guarded Suspension模式:等待唤醒机制的规范实现
32 | Balking模式:再谈线程安全的单例模式
33 | Thread-Per-Message模式:最简单实用的分工方法
34 | Worker Thread模式:如何避免重复创建线程?
35 | 两阶段终止模式:如何优雅地终止线程?
36 | 生产者-消费者模式:用流水线思想提高效率
37 | 设计模式模块热点问题答疑
38 | 案例分析(一):高性能限流器Guava RateLimiter
39 | 案例分析(二):高性能网络应用框架Netty
40 | 案例分析(三):高性能队列Disruptor
41 | 案例分析(四):高性能数据库连接池HiKariCP
42 | Actor模型:面向对象原生的并发模型
43 | 软件事务内存:借鉴数据库的并发经验
44 | 协程:更轻量级的线程
45 | CSP模型:Golang的主力队员
当前位置:
首页>>
技术小册>>
Java并发编程实战
小册名称:Java并发编程实战
### 21 | 原子类:无锁工具类的典范 在Java的并发编程世界中,线程安全是绕不开的重要话题。为了高效且安全地处理多线程环境下的数据共享与操作,Java提供了多种同步机制,如`synchronized`关键字、`Lock`接口及其实现类(如`ReentrantLock`)等。然而,这些同步机制虽然有效,但在高并发场景下可能会因为线程竞争和上下文切换而导致性能瓶颈。为了克服这一难题,Java并发包(`java.util.concurrent.atomic`)提供了一系列基于CAS(Compare-And-Swap,比较并交换)算法的原子类,它们作为无锁编程的典范,为开发者提供了一种更轻量级、更高性能的线程安全解决方案。 #### 一、原子类的基本概念 原子类是指那些能够确保在多线程环境中,单个操作(如增减、更新等)的原子性,即这些操作在执行过程中不会被线程调度机制中断的类。Java并发包中的原子类大多是通过CAS算法实现的,CAS算法是一种基于硬件对并发操作的原语支持,它能够在不锁定任何资源的情况下实现多线程之间的数据同步。 #### 二、CAS算法原理 CAS算法包含三个操作数: - **内存位置(V)**:表示要更新的变量在内存中的位置。 - **预期原值(A)**:表示线程认为该位置应该有的旧值。 - **新值(B)**:表示线程希望将该位置更新的新值。 当且仅当内存位置的值与预期原值相匹配时,处理器会自动将该位置值更新为新值,这个操作是原子的。如果内存位置的值与预期原值不匹配,说明该位置的值已经被其他线程修改过,此时CAS操作会失败,并返回当前内存位置的实际值。 #### 三、Java并发包中的原子类 Java并发包`java.util.concurrent.atomic`提供了丰富的原子类,包括但不限于: - **`AtomicInteger`、`AtomicLong`**:用于执行整数的原子更新操作,如自增、自减、添加等。 - **`AtomicBoolean`**:用于执行布尔值的原子更新操作。 - **`AtomicReference`**:用于执行对象的原子更新操作,是原子引用类型的基类。 - **`AtomicStampedReference`**:通过引入“版本戳”的概念,解决了ABA问题(一个位置上的值被先改为A,再改为B,最后又改回A,但CAS操作认为它从未被改变)。 - **`AtomicIntegerArray`、`AtomicLongArray`、`AtomicReferenceArray`**:分别是对整数数组、长整型数组、对象数组的原子操作类。 - **`AtomicIntegerFieldUpdater`、`AtomicLongFieldUpdater`、`AtomicReferenceFieldUpdater`**:这些类提供了基于反射的原子更新功能,允许对指定类的指定`volatile`字段进行原子更新。 #### 四、原子类的使用场景与示例 ##### 1. 计数器 `AtomicInteger`是原子类中最为常用的一个,它常被用作计数器。例如,统计网站的访问量时,可以使用`AtomicInteger`来确保计数的准确性: ```java AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); // 原子地增加计数器的值并返回新值 } public int getCount() { return count.get(); // 获取当前计数值 } ``` ##### 2. 累加器 除了简单的自增操作外,`AtomicInteger`还支持其他复杂的原子操作,如累加: ```java AtomicInteger sum = new AtomicInteger(0); public void add(int delta) { sum.addAndGet(delta); // 原子地将delta加到当前值上,并返回新值 } public int getSum() { return sum.get(); // 获取当前累加和 } ``` ##### 3. 标志位 `AtomicBoolean`用于表示一个布尔值,常用于控制线程的执行流程,如: ```java AtomicBoolean running = new AtomicBoolean(true); public void stopTask() { running.set(false); // 原子地设置标志位为false } public boolean isRunning() { return running.get(); // 原子地获取标志位的值 } public void executeTask() { while (isRunning()) { // 执行任务 } } ``` ##### 4. 复杂对象的原子更新 对于复杂对象的原子更新,可以使用`AtomicReference`或其变体。例如,更新一个对象的某个属性,但希望这个更新过程是原子的: ```java class Data { private int value; // 省略构造函数、getter和setter } AtomicReference<Data> dataRef = new AtomicReference<>(new Data(0)); public void updateValue(int newValue) { Data currentData; Data newData; do { currentData = dataRef.get(); newData = new Data(currentData.getValue() + newValue); } while (!dataRef.compareAndSet(currentData, newData)); // 尝试原子更新 } ``` #### 五、原子类的性能与优化 虽然原子类提供了高效的线程安全操作,但它们并非没有代价。CAS操作可能因为频繁失败而导致“忙等待”(Busy-Waiting),消耗CPU资源。此外,CAS操作通常只涉及单个变量的修改,对于涉及多个变量的复合操作,可能需要额外的同步机制来保证操作的原子性。 为了优化性能,可以采取以下措施: - **减少CAS操作频率**:通过合理设计算法和数据结构,减少不必要的CAS操作。 - **避免高冲突场景**:尽量设计低冲突的数据访问模式,减少CAS操作的失败率。 - **结合使用其他同步机制**:对于复杂操作,可以结合使用原子类和传统的锁机制,以实现更好的性能和更高的安全性。 #### 六、总结 Java并发包中的原子类是无锁编程的典范,它们通过CAS算法实现了对单个变量操作的原子性,为多线程环境下的数据同步提供了一种轻量级、高性能的解决方案。然而,开发者在使用原子类时,也需要注意其潜在的性能问题和适用场景,以充分利用其优势,避免不必要的性能开销。通过深入理解原子类的原理和使用方法,我们可以在Java并发编程中更加灵活地应对各种挑战,编写出既高效又安全的并发程序。
上一篇:
20 | 并发容器:都有哪些“坑”需要我们填?
下一篇:
22 | Executor与线程池:如何创建正确的线程池?
该分类下的相关小册推荐:
Java语言基础1-基础知识
Mybatis合辑1-Mybatis基础入门
Mybatis合辑2-Mybatis映射文件
Java性能调优实战
Java面试指南
Java语言基础5-面向对象初级
Mybatis合辑3-Mybatis动态SQL
Java语言基础13-类的加载和反射
java源码学习笔记
Java语言基础2-运算符
Java必知必会-JDBC
SpringBoot零基础到实战