原子类之AtomicInteger

前言

AtomicInteger 类底层存储一个int值,并提供方法对该int值进行原子操作。AtomicInteger 作为java.util.concurrent.atomic包的一部分,从Java 1.5开始引入。

接下来我们先从三个问题入手,对AtomicInteger概念有一个基本的了解。

一、概述

(1)什么是原子操作呢?

所谓原子操作,就是一个独立且不可分割的操作。AtomicInteger 工具类提供了对整数操作的原子封装。

(2)为什么要对整数操作进行原子封装呢?

在 java 中,当我们在多线程情况下,对一个整型变量做加减操作时,如果不加任何的多线程并发控制,大概率会出现线程安全问题,也就是说当多线程同时操作一个整型变量的增减时,会出现运算结果错误的问题。AtomicInteger 工具类就是为了简化整型变量的同步处理而诞生的。

(3)什么场景下需要使用AtomicInteger?

  • 多线程并发场景下操作一个计数器,需要保证计数器操作的原子性。
  • 进行数值比较,如果给定值与当前值相等,进行数值的更新操作,并实现操作的非阻塞算法。

二、AtomicInteger基础用法

通过AtomicInteger构造方法,可以创建一个AtomicInteger对象,该对象的初始值默认为0。AtomicInteger提供get和set方法,获取底层int整数值,与设置int整数值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.concurrent.atomic.AtomicInteger;

/**
* Created by xiaowei
* Date 2022/11/5
* Description AtomicInteger基本用法
*/
public class Test01 {
public static void main(String[] args) {
//首先创建一个AtomicInteger对象
AtomicInteger atomicInteger = new AtomicInteger();
//在操作之前先赋值,如果不显示赋值则默认为0,就像 int 型变量使用前做初始化赋值一样。
atomicInteger.set(100);

// 之后可以调用各种方法进行增减操作
// 获取当前值
System.out.println(atomicInteger.get());
// 先获取当前值,之后再对原值加100,返回之前的旧值
System.out.println(atomicInteger.getAndAdd(100));
// 先获取当前值,之后再对原值减1,返回旧值
System.out.println(atomicInteger.getAndDecrement());
}
}

运行结果

1
2
3
100
100
200

三、原子计数器场景

AtomicInteger作为一个计数器使用,AtomicInteger提供了若干方法进行加法、减法的原子操作。

比如从一个map里面获取值,用get()方法,这是第一个操作;

获取到值之后给这个值加上n,这是第二个操作;

将进行过加法运算的值,再次放入map里面是第三个操作。

所谓操作的原子性是指:在多线程并发的场景下,上面的三个操作是原子性的,也就是不可分割的。

不会出现A线程get了数值,B线程同时也get到了该数值,两个线程同时为该值做运算并先后再次放入的情况,这种情况对于AtomicInteger而言是不会出现的,AtomicInteger操作是线程安全的、不可分割的。

AtomicInteger相关方法

方法 说明
addAndGet() 将给定的值加到当前值上,并在加法后返回新值,并保证操作的原子性。
getAndAdd() 将给定的值加到当前值上,并返回旧值,并保证操作的原子性。
incrementAndGet() 将当前值增加1,并在增加后返回新值。它相当于++i操作,并保证操作的原子性。
getAndIncrement() 将当前值增加1并返回旧值。相当于i++操作,并保证操作的原子性。
decrementAndGet() 将当前值减去1,并在减去后返回新值,相当于--i操作,并保证操作的原子性。
getAndDecrement() 将当前值减去1,并返回旧值。它相当于 i--操作,并保证操作的原子性。

使用示例

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
import java.util.concurrent.atomic.AtomicInteger;

/**
* Created by xiaowei
* Date 2022/11/5
* Description AtomicInteger示例操作
*/
public class Test02 {
public static void main(String[] args) {
AtomicInteger i = new AtomicInteger(0);

// 获取并自增(i = 0, 结果 i = 1, 返回 0),类似于 i++
System.out.println(i.getAndIncrement());

// 自增并获取(i = 1, 结果 i = 2, 返回 2),类似于 ++i
System.out.println(i.incrementAndGet());

// 自减并获取(i = 2, 结果 i = 1, 返回 1),类似于 --i
System.out.println(i.decrementAndGet());

// 获取并自减(i = 1, 结果 i = 0, 返回 1),类似于 i--
System.out.println(i.getAndDecrement());

// 获取并加值(i = 0, 结果 i = 5, 返回 0)
System.out.println(i.getAndAdd(5));

// 加值并获取(i = 5, 结果 i = 0, 返回 0)
System.out.println(i.addAndGet(-5));

// 获取并更新(i = 0, p 为 i 的当前值, 结果 i = -2, 返回 0)
// 其中函数中的操作能保证原子,但函数需要无副作用
System.out.println(i.getAndUpdate(p -> p - 2));

// 更新并获取(i = -2, p 为 i 的当前值, 结果 i = 0, 返回 0)
// 其中函数中的操作能保证原子,但函数需要无副作用
System.out.println(i.updateAndGet(p -> p + 2));

// 获取并计算(i = 0, p 为 i 的当前值, x 为参数1, 结果 i = 10, 返回 0)
// 其中函数中的操作能保证原子,但函数需要无副作用
// getAndUpdate 如果在 lambda 中引用了外部的局部变量,要保证该局部变量是 final 的
// getAndAccumulate 可以通过 参数1 来引用外部的局部变量,但因为其不在 lambda 中因此不必是 final
System.out.println(i.getAndAccumulate(10, (p, x) -> p + x));

// 计算并获取(i = 10, p 为 i 的当前值, x 为参数1, 结果 i = 0, 返回 0)
// 其中函数中的操作能保证原子,但函数需要无副作用
System.out.println(i.accumulateAndGet(-10, (p, x) -> p + x));
}
}

四、数值比对及交换操作

compareAndSet操作将一个内存位置的内容与一个给定的值进行比较,只有当它们相同时,才会将该内存位置的内容修改为一个给定的新值。这个过程是以单个原子操作的方式完成的。

语法

  • compareAndSet方法:如果当前值==预期值,则将值设置为给定的更新值。
1
boolean compareAndSet(int expect, int update)
  • expect是预期值
  • update是更新值

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.concurrent.atomic.AtomicInteger;

/**
* Created by xiaowei
* Date 2022/11/5
* Description compareAndSet() 方法示例
*/
public class Test03 {
public static void main(String[] args) {
//初始值为100的atomic Integer
AtomicInteger atomicInteger = new AtomicInteger(100);

//当前值100 = 预期值100,所以设置atomicInteger=110
boolean isSuccess = atomicInteger.compareAndSet(100,110);
System.out.println(isSuccess); //输出结果为true表示操作成功

//当前值110 = 预期值100? 不相等,所以atomicInteger仍然等于110
isSuccess = atomicInteger.compareAndSet(100,120);
System.out.println(isSuccess); //输出结果为false表示操作失败
}
}

运行结果

1
2
true
false

总结

AtomicInteger可以帮助我们在不使用synchronized同步锁的情况下,实现在多线程场景下int数值操作的线程安全,操作的原子性。并且使用AtomicInteger来实现int数值的原子操作,远比使用synchronized同步锁效率更高。

java.util.concurrent.atomic包不仅提供了AtomicInteger,还提供了AtomicBoolean布尔原子操作类、AtomicLong长整型布尔原子操作类、AtomicReference对象原子操作类、AtomicIntegerArray整型数组原子操作类、AtomicLongArray长整型数组原子操作类、AtomicReferenceArray对象数组原子操作类。


原子类之AtomicInteger
http://example.com/2023/03/29/原子类之AtomicInteger/
作者
程序员小魏
发布于
2023年3月29日
许可协议