Skip to content

Commit b392abd

Browse files
committed
Update atomic-classes.md
1 parent fe697ad commit b392abd

File tree

1 file changed

+41
-76
lines changed

1 file changed

+41
-76
lines changed

docs/java/concurrent/atomic-classes.md

+41-76
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,45 @@ tag:
77

88
## Atomic 原子类介绍
99

10-
Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰
10+
`Atomic` 翻译成中文是“原子”的意思。在化学上,原子是构成物质的最小单位,在化学反应中不可分割。在编程中,`Atomic` 指的是一个操作具有原子性,即该操作不可分割、不可中断。即使在多个线程同时执行时,该操作要么全部执行完成,要么不执行,不会被其他线程看到部分完成的状态
1111

12-
所以,所谓原子类说简单点就是具有原子/原子操作特征的类
12+
原子类简单来说就是具有原子性操作特征的类
1313

14-
并发包 `java.util.concurrent` 的原子类都存放在`java.util.concurrent.atomic`下,如下图所示。
14+
`java.util.concurrent.atomic` 包中的 `Atomic` 原子类提供了一种线程安全的方式来操作单个变量。
15+
16+
`Atomic` 类依赖于 CAS(Compare-And-Swap,比较并交换)乐观锁来保证其方法的原子性,而不需要使用传统的锁机制(如 `synchronized` 块或 `ReentrantLock`)。
17+
18+
这篇文章我们直接介绍 Atomic 原子类的概念,具体实现原理可以阅读笔者写的这篇文章:[CAS 详解](./cas.md)
1519

1620
![JUC原子类概览](https://oss.javaguide.cn/github/javaguide/java/JUC%E5%8E%9F%E5%AD%90%E7%B1%BB%E6%A6%82%E8%A7%88.png)
1721

18-
根据操作的数据类型,可以将 JUC 包中的原子类分为 4 类
22+
根据操作的数据类型,可以将 JUC 包中的原子类分为 4 类
1923

20-
**基本类型**
24+
**1、基本类型**
2125

2226
使用原子的方式更新基本类型
2327

2428
- `AtomicInteger`:整型原子类
2529
- `AtomicLong`:长整型原子类
2630
- `AtomicBoolean`:布尔型原子类
2731

28-
**数组类型**
32+
**2、数组类型**
2933

3034
使用原子的方式更新数组里的某个元素
3135

3236
- `AtomicIntegerArray`:整型数组原子类
3337
- `AtomicLongArray`:长整型数组原子类
3438
- `AtomicReferenceArray`:引用类型数组原子类
3539

36-
**引用类型**
40+
**3、引用类型**
3741

3842
- `AtomicReference`:引用类型原子类
3943
- `AtomicMarkableReference`:原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来,~~也可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题~~
4044
- `AtomicStampedReference`:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
4145

4246
**🐛 修正(参见:[issue#626](https://github.com/Snailclimb/JavaGuide/issues/626)** : `AtomicMarkableReference` 不能解决 ABA 问题。
4347

44-
**对象的属性修改类型**
48+
**4、对象的属性修改类型**
4549

4650
- `AtomicIntegerFieldUpdater`:原子更新整型字段的更新器
4751
- `AtomicLongFieldUpdater`:原子更新长整型字段的更新器
@@ -57,7 +61,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是
5761

5862
上面三个类提供的方法几乎相同,所以我们这里以 `AtomicInteger` 为例子来介绍。
5963

60-
**AtomicInteger 类常用方法**
64+
**`AtomicInteger` 类常用方法**
6165

6266
```java
6367
public final int get() //获取当前的值
@@ -66,90 +70,51 @@ public final int getAndIncrement()//获取当前的值,并自增
6670
public final int getAndDecrement() //获取当前的值,并自减
6771
public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
6872
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
69-
public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值
73+
public final void lazySet(int newValue)//最终设置为newValue, lazySet 提供了一种比 set 方法更弱的语义,可能导致其他线程在之后的一小段时间内还是可以读到旧的值,但可能更高效
7074
```
7175

7276
**`AtomicInteger` 类使用示例** :
7377

7478
```java
75-
import java.util.concurrent.atomic.AtomicInteger;
76-
77-
public class AtomicIntegerTest {
78-
79-
public static void main(String[] args) {
80-
int temvalue = 0;
81-
AtomicInteger i = new AtomicInteger(0);
82-
temvalue = i.getAndSet(3);
83-
System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:0; i:3
84-
temvalue = i.getAndIncrement();
85-
System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:3; i:4
86-
temvalue = i.getAndAdd(5);
87-
System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:4; i:9
88-
}
79+
// 初始化 AtomicInteger 对象,初始值为 0
80+
AtomicInteger atomicInt = new AtomicInteger(0);
8981

90-
}
91-
```
82+
// 使用 getAndSet 方法获取当前值,并设置新值为 3
83+
int tempValue = atomicInt.getAndSet(3);
84+
System.out.println("tempValue: " + tempValue + "; atomicInt: " + atomicInt);
9285

93-
### 基本数据类型原子类的优势
86+
// 使用 getAndIncrement 方法获取当前值,并自增 1
87+
tempValue = atomicInt.getAndIncrement();
88+
System.out.println("tempValue: " + tempValue + "; atomicInt: " + atomicInt);
9489

95-
通过一个简单例子带大家看一下基本数据类型原子类的优势
90+
// 使用 getAndAdd 方法获取当前值,并增加指定值 5
91+
tempValue = atomicInt.getAndAdd(5);
92+
System.out.println("tempValue: " + tempValue + "; atomicInt: " + atomicInt);
9693

97-
**1、多线程环境不使用原子类保证线程安全(基本数据类型)**
94+
// 使用 compareAndSet 方法进行原子性条件更新,期望值为 9,更新值为 10
95+
boolean updateSuccess = atomicInt.compareAndSet(9, 10);
96+
System.out.println("Update Success: " + updateSuccess + "; atomicInt: " + atomicInt);
9897

99-
```java
100-
class Test {
101-
private volatile int count = 0;
102-
//若要线程安全执行执行count++,需要加锁
103-
public synchronized void increment() {
104-
count++;
105-
}
98+
// 获取当前值
99+
int currentValue = atomicInt.get();
100+
System.out.println("Current value: " + currentValue);
106101

107-
public int getCount() {
108-
return count;
109-
}
110-
}
102+
// 使用 lazySet 方法设置新值为 15
103+
atomicInt.lazySet(15);
104+
System.out.println("After lazySet, atomicInt: " + atomicInt);
111105
```
112106

113-
**2、多线程环境使用原子类保证线程安全(基本数据类型)**
107+
输出:
114108

115109
```java
116-
class Test2 {
117-
private AtomicInteger count = new AtomicInteger();
118-
119-
public void increment() {
120-
count.incrementAndGet();
121-
}
122-
//使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
123-
public int getCount() {
124-
return count.get();
125-
}
126-
}
127-
110+
tempValue: 0; atomicInt: 3
111+
tempValue: 3; atomicInt: 4
112+
tempValue: 4; atomicInt: 9
113+
Update Success: true; atomicInt: 10
114+
Current value: 10
115+
After lazySet, atomicInt: 15
128116
```
129117

130-
### AtomicInteger 线程安全原理简单分析
131-
132-
`AtomicInteger` 类的部分源码:
133-
134-
```java
135-
// setup to use Unsafe.compareAndSwapInt for updates(更新操作时提供“比较并替换”的作用)
136-
private static final Unsafe unsafe = Unsafe.getUnsafe();
137-
private static final long valueOffset;
138-
139-
static {
140-
try {
141-
valueOffset = unsafe.objectFieldOffset
142-
(AtomicInteger.class.getDeclaredField("value"));
143-
} catch (Exception ex) { throw new Error(ex); }
144-
}
145-
146-
private volatile int value;
147-
```
148-
149-
`AtomicInteger` 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。
150-
151-
CAS 的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。UnSafe 类的 `objectFieldOffset()` 方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址。另外 value 是一个 volatile 变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。
152-
153118
## 数组类型原子类
154119

155120
使用原子的方式更新数组里的某个元素

0 commit comments

Comments
 (0)