Atomic

基本类型

常用API

方法 解释
public final int get() 获取当前的值
public final int getAndSet(int newValue) 获取到当前的值,并设置新的值
public final int getAndIncrement() 获取当前的值,并自增
public final int getAndDecrement() 获取到当前的值,并自减
public final int getAndAdd(int delta) 获取到当前的值,并加上预期的值
public final int incrementAndGet( ) 返回的是加1后的值
boolean compareAndSet(int expect,int update) 如果输入的数值等于预期值,返回true

AtomicInteger 解决 i++

CountDownLatch 如何在程序中使用

在没使用 CountDownLatch 时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package tech.chen.juccode.a14;

import java.util.concurrent.atomic.AtomicInteger;

/**
* @Date 2022/8/28 17:04
* @Author c-z-k
*/
public class CountDownDemo {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
new Thread(()->{
for (int j = 0; j < 100; j++) {
atomicInteger.getAndIncrement();
}
}).start();
}
System.out.println("atomicInteger.get() = " + atomicInteger.get());
}
}

结果:

atomicInteger.get() = 4900

发现使用 AtomicInteger 居然没有得到理想值 5000,是 AtomicInteger 的问题吗?显然不可能,实际原因是,累加还没结束,主线程就打印出了结果,不信你可以等两秒。但是,实际操作我们不可能设置一个固定的睡眠时间。CountDownLatch 出现的意义也在此。

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
package tech.chen.juccode.a14;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
* @Date 2022/8/28 17:04
* @Author c-z-k
*/
public class CountDownDemo {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
private static CountDownLatch countDownLatch = new CountDownLatch(50);
public static void main(String[] args) {
for (int i = 0; i <50; i++) {
new Thread(()->{
for (int j = 0; j < 100; j++) {
atomicInteger.getAndIncrement();
}
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("atomicInteger.get() = " + atomicInteger.get());
}
}

atomicInteger.get() = 5000

AtomicBoolean中断标识

AtomicBoolean可以作为中断标识停止线程的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//线程中断机制的实现方法
public class AtomicBooleanDemo {
public static void main(String[] args) {
AtomicBoolean atomicBoolean=new AtomicBoolean(false);

new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t"+"coming.....");
while(!atomicBoolean.get()){
System.out.println("==========");
}
System.out.println(Thread.currentThread().getName()+"\t"+"over.....");
},"A").start();

new Thread(()->{
atomicBoolean.set(true);
},"B").start();
}
}

AtomicLong

AtomicLong 的底层是 CAS+自旋锁 的思想,适用于低并发的全局计算,高并发后性能急剧下降,原因如下:N个线程CAS操作修改线程的值,每次只有一个成功过,其他N-1失败,失败的不停的自旋直到成功,这样大量失败自旋的情况,一下子 cpu 就打高了(AtomicLong的自旋会成为瓶颈)

在高并发的情况下,我们使用LoadAdder

数组类型

AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package tech.chen.juccode.a14;

import java.util.concurrent.atomic.AtomicIntegerArray;

/**
* @Date 2022/8/28 18:30
* @Author c-z-k
*/
public class AtomicArray {
public static void main(String[] args) {
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
for (int i = 0; i < atomicIntegerArray.length(); i++) {
int i1 = atomicIntegerArray.get(i);
System.out.println("i1 = " + i1);
}

int i = atomicIntegerArray.incrementAndGet(0);
System.out.println("i = " + i);
int andAdd = atomicIntegerArray.addAndGet(1, 4);
System.out.println("andAdd = " + andAdd);
}
}

引用类型

AtomicReference、AtomicStampedReference、AtomicMarkableReference

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
package tech.chen.juccode.a14;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
* @Date 2022/8/28 18:39
* @Author c-z-k
*/
public class AtomicReferenceForLock {

private static AtomicReference<Thread> atomicReference = new AtomicReference<>();

private static void lock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t" + "coming ");
while (!atomicReference.compareAndSet(null, thread)) {}
}

private static void unlock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t" + "take over ");
atomicReference.compareAndSet(thread,null);
}

public static void main(String[] args) {
new Thread(()->{
lock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
unlock();
},"t1").start();
new Thread(()->{
lock();
unlock();
},"t2").start();
}
}

AtomicStampedReference 这个带有戳记的 引用原子类 在上一篇已经讲过,这里就不赘述了

AtomicMarkableReference

  1. 原子更新带有标志位的引用类型对象
  2. 解决是否修改(它的定义就是将状态戳简化位 true|false ) ,类似一次性筷子
  3. 状态戳 (true/false) 原子引用
  4. 不建议用它解决 ABA 问题
  5. Markable – true、false 是否修改过,跟 AtomicStampedReference 的区别就是 它只负责监督一次改变。

对象属性修改类型

目的 : 以一种线程安全的方式操作非线程安全对象内的某些字段 。判断是否可以不要锁定整个对象,减少锁定的范围,只关注长期、敏感性变化的某一个字段,而不是整个对象,达到精确加锁+节约内存的目的

条件:

  • 更新的对象属性必须使用 public volatile 修饰符
  • 因为对象的属性修改类型原子类都是抽象类 ,所以每次使用都必须使用静态方法 newUpdater( ) 创建一个更新器,并且需要设置想要更新的类和属性
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
package tech.chen.juccode.a14;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
* @Date 2022/8/29 8:22
* @Author c-z-k
*/
public class AtomicFieldUpdaterDemo {
public static void main(String[] args) {
User user = new User();
AtomicIntegerFieldUpdater<User> atomicIntegerFieldUpdaterAge =
AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
AtomicReferenceFieldUpdater atomicReferenceFieldUpdaterName =
AtomicReferenceFieldUpdater.newUpdater(User.class,String.class,"name");
int i = atomicIntegerFieldUpdaterAge.get(user);
System.out.println("i = " + i);//i = 0
Object o = atomicReferenceFieldUpdaterName.get(user);
System.out.println("o = " + o);//o = czk
int newAge = atomicIntegerFieldUpdaterAge.accumulateAndGet(user, 18, (x, y) -> x + y);
System.out.println("newAge = " + newAge);//newAge = 18
boolean czk = atomicReferenceFieldUpdaterName.compareAndSet(user, "czk", "0205");
System.out.println("czk = " + czk);//czk = true
}
}
class User{
public volatile int age = 0;
public volatile String name = "czk";
}

运行结果

i = 0
o = czk
newAge = 18

原子操作增强类

DoubleAccumulator 、DoubleAdder 、LongAccumulator 、LongAdder

常用API

方法 解释
void add(long x) 将当前的value加1
void increment( ) 将当前的value加1
void decrement( ) 将当前value减1
long sum( ) 返回当前的值,特别注意,在没有并发更新value的情况下sum会返回一个精确值,在存在并发的情况下,sum不保证返回精确值
long longvale 等价于long sum( )。将value重置为0,可用于替换重新new一个LongAdder,但此方法只可以在没有并发更新的情况下使用
long sumThenReset() 获取当前value,并将value重置为0
  1. LongAdder只能用来计算加法、减法,且从零开始计算
  2. LongAccumulator提供了自定义的函数操作
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package tech.chen.juccode.a14;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
* @Date 2022/8/29 8:53
* @Author c-z-k
*/
public class LongAdderCalcDemo {
public static final int SIZE_THREAD=50;
public static final int _1w=10000;

public static void main(String[] args) {
synchronizedTest();
atomicIntegerTest();
atomicLongTest();
longAdderTest();
longAccumulatorTest();
}

private static void longAdderTest() {
// 50个线程和每个线程点在100w次
CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD);
ClickNumber clickNumber=new ClickNumber();
long startTime = System.currentTimeMillis();
for (int i = 1 ; i <=SIZE_THREAD ; i++) {
new Thread(()->{
try{
for (int j = 1; j <=100*_1w; j++) {
//clickNumber.add_synchronized();
clickNumber.add_longAdder();
}
}finally {
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.print("-----add_longAdder:"+(endTime-startTime)+"毫秒"+"\t");
System.out.println(clickNumber.adder.longValue());
}
private static void synchronizedTest() {
// 50个线程和每个线程点在100w次
CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD);
ClickNumber clickNumber=new ClickNumber();
long startTime = System.currentTimeMillis();
for (int i = 1 ; i <=SIZE_THREAD ; i++) {
new Thread(()->{
try{
for (int j = 1; j <=100*_1w; j++) {
//clickNumber.add_synchronized();
clickNumber.add_synchronized();
}
}finally {
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.print("-----add_synchronized:"+(endTime-startTime)+"毫秒"+"\t");
System.out.println(clickNumber.number);
}
private static void atomicIntegerTest() {
// 50个线程和每个线程点在100w次
CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD);
ClickNumber clickNumber=new ClickNumber();
long startTime = System.currentTimeMillis();
for (int i = 1 ; i <=SIZE_THREAD ; i++) {
new Thread(()->{
try{
for (int j = 1; j <=100*_1w; j++) {
//clickNumber.add_atomicInteger();
clickNumber.add_atomicInteger();
}
}finally {
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.print("-----add_atomicInteger:"+(endTime-startTime)+"毫秒"+"\t");
System.out.println(clickNumber.atomicInteger.get());
}
private static void atomicLongTest() {
// 50个线程和每个线程点在100w次
CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD);
ClickNumber clickNumber=new ClickNumber();
long startTime = System.currentTimeMillis();
for (int i = 1 ; i <=SIZE_THREAD ; i++) {
new Thread(()->{
try{
for (int j = 1; j <=100*_1w; j++) {
//clickNumber.add_atomicInteger();
clickNumber.add_atomicLong();
}
}finally {
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.print("-----add_atomicLong:"+(endTime-startTime)+"毫秒"+"\t");
System.out.println(clickNumber.atomicLong.get());
}
private static void longAccumulatorTest() {
// 50个线程和每个线程点在100w次
CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD);
ClickNumber clickNumber=new ClickNumber();
long startTime = System.currentTimeMillis();
for (int i = 1 ; i <=SIZE_THREAD ; i++) {
new Thread(()->{
try{
for (int j = 1; j <=100*_1w; j++) {
//clickNumber.add_longAccumulater();
clickNumber.add_longAccumulater();
}
}finally {
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.print("-----add_longAccumulater:"+(endTime-startTime)+"毫秒"+"\t");
System.out.println(clickNumber.accumulator.get());
}
}
class ClickNumber{
int number=0;

//(1). 使用synchronized实现number++
public synchronized void add_synchronized(){
number++;
}
//(2). 使用AtomicInteger
AtomicInteger atomicInteger=new AtomicInteger();
public void add_atomicInteger(){
atomicInteger.incrementAndGet();
}
//(3). 使用AtomicLong
AtomicLong atomicLong=new AtomicLong();
public void add_atomicLong(){
atomicLong.incrementAndGet();
}
//(4). 使用LongAdder
LongAdder adder=new LongAdder();
public void add_longAdder(){
adder.increment();
}
//(5). 使用LongAccumulater
LongAccumulator accumulator=new LongAccumulator((x, y)->x+y,0);
public void add_longAccumulater(){
accumulator.accumulate(1);
}
}

运行结果:

—–add_synchronized:4089毫秒 50000000
—–add_atomicInteger:822毫秒 50000000
—–add_atomicLong:831毫秒 50000000
—–add_longAdder:187毫秒 50000000
—–add_longAccumulater:123毫秒 50000000