一、前言

在上篇 《Java 多线程开发之 volatile(一)》 文章中介绍了 volatile 的相关内容,它是一个轻量级的锁,但不支持原子操作。

本篇将介绍原子操作相关内容。

二、基本概念

2.1 CAS 算法

CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。

CAS 是一种无锁的非阻塞算法的实现,该算法包含了 3 个操作数

1
2
3
需要读写的内存值 V
进行比较的值 A
拟写入的新值 B

当且仅当 V 的值等于 A 时, CAS 通过原子方式用新值 B 来更新 V 的值,否则不会执行任何操作。

三、演示与分析

3.1 案例演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class CASTest {

public static void main(String[] args) {

DemoRunnable dr = new DemoRunnable();

for (int i = 0; i < 10; i++) {
new Thread(dr).start();
}
}
}

class DemoRunnable implements Runnable {

private volatile int count = 0;

@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程-count:" + (++count));
}

}

打印结果:

1
2
3
4
5
6
7
8
9
10
子线程-count:1
子线程-count:1
子线程-count:2
子线程-count:5
子线程-count:7
子线程-count:6
子线程-count:8
子线程-count:4
子线程-count:4
子线程-count:3

从结果我们可以发现 count 的值没有加到 10,且出现多个线程累加 count 值重复的问题,即出现了线程安全问题。

3.2 问题分析

其实,在 Java 语言中执行 ++ 操作实际上在底层被拆分为三个步骤,即“读-改-写”。

笔者在初学 Java 时遇到如下一个问题:

1
2
3
int i = 10;
i = i++;
System.out.println(i);

打印的结果依然为 10。

因为 i = i++ 在底层操作如下:

1
2
3
int temp = i; // 读
i = i + 1; // 改
i = temp; // 写

同理,在上文的代码中,System.out.println("子线程-count:" + (++count)); 同样被拆分为多个步骤,无法保证操作的原子性,从而当多个线程修改 count 时出现对旧值重复累加操作,进而出现打印相同结果的问题。

四、解决方案

在 java.util.concurrent.atomic 包中,提供了很多支持原子操作的类。如:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference 等。

这些类底层通过 CAS 算法实现。

修改 DemoRunnable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class DemoRunnable implements Runnable {

private volatile AtomicInteger ai = new AtomicInteger();

@Override
public void run() {

try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("子线程-count:" + (ai.incrementAndGet()));
}

}

将 int count 改成 AtomicInteger ai。

代码执行 N 次都能正常将 ai 的值累加成 10 。

五、模拟 CAS 算法

现在,根据上文介绍的 CAS 算法步骤来模拟 CAS 算法的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class CompareAndSwapTest {

public static void main(String[] args) {

CompareAndSwap cas = new CompareAndSwap();

for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int expectedValue = cas.get();
boolean result = cas.compareAndSet(expectedValue,(int) (Math.random() * 100));
System.out.println("结果:" + result);
}
}).start();
}
}
}

class CompareAndSwap {

private int value;

/**
* 获取内存值
*
* @return
*/
public synchronized int get() {
return value;
}

/**
* 设置
*
* @param expectedValue 预估值
* @param newValue 新值
*
* @return
*/
public synchronized boolean compareAndSet(int expectedValue, int newValue) {
return expectedValue == compareAndSwap(expectedValue, newValue);
}

/**
* 比较
*
* @param expectedValue 预估值
* @param newValue 新值
*
* @return
*/
private synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;

if (oldValue == expectedValue) {
value = newValue;
}

return oldValue;
}
}

注意:此处模拟代码使用 synchronized 关键字只是模拟演示效果,并非 Java 底层具体实现方式。