AtomicInteger是在多线程环境下使用的,当您需要确保只有一个线程可以更新int变量时使用。优点是不需要外部同步,因为修改它值的操作以线程安全的方式执行。
考虑以下代码:
private int count;
public int updateCounter() {
return ++count;
}
如果有多个线程调用updateCounter方法,有可能其中一些线程会收到相同的值。其原因是++count操作不是原子性的,因为它不只是一个操作,而是由三个操作组成:读取计数器,将1加到这个值上,然后将其写回
。多个调用线程可能会看到变量为未修改为最新值。private AtomicInteger count = new AtomicInteger(0);
public int updateCounter() {
return count.incrementAndGet();
}
incrementAndGet
方法保证原子地递增存储的值并在不使用任何外部同步的情况下返回其值。
如果您的值从未更改,则无需使用AtomicInteger,int足以使用。
AtomicInteger是线程安全的(事实上,java.util.concurrent.atomic包中的所有类都是线程安全的),而普通整数则不是线程安全的。
当您在多线程环境中使用'Integer'变量时(使其线程安全),您需要使用'synchronized'和'volatile'关键字,而对于原子整数,您不需要使用'synchronized'和'volatile'关键字,因为原子整数会处理线程安全性。
此外,我建议您参考下面有关相同主题的有用教程: http://tutorials.jenkov.com/java-concurrency/compare-and-swap.html
请参考下面的oracle文档以获取有关'atomic'包的更多信息: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html
虽然不可变对象在定义上是线程安全的,但可变对象也可以是线程安全的。
这正是Atomic...
类(如AtomicInteger
、AtomicBoolean
等)的目的所在。
各种...get...
和...set...
方法允许线程安全地访问和修改对象。
毫不奇怪,该类声明在java.util.concurrent
包中。
您只需浏览java.util.concurrent.atomic
包的API:
一组支持单变量无锁线程安全编程的小型工具类。
Number
。这完全取决于用途。 - Mena考虑一个变量
int myInt = 3;
AtomicInteger
与myInt
相关联。
Integer
与3
相关联。
换句话说,您的变量是可变的,可以更改其值。而值为3的整数文字是一个常量、不可变表达式。
整数是字面量的对象表示形式,因此是不可变的,基本上只能读取它们。
AtomicInteger
是这些值的容器。您可以读取和设置它们,就像将值分配给变量一样。但与更改int
变量的值不同,对AtomicInteger
的操作是原子的。
例如,以下操作不是原子性的:
if(myInt == 3) {
myInt++;
}
AtomicInteger myInt = new AtomicInteger(3);
//atomic
myInt.compareAndSet(3, 4);
AtomicInteger
无法引用 int
变量,因此你在前三行的示例只会让人感到困惑。 - Kayaman我认为AtomicInteger和普通的不可变整数之间的主要区别将在我们理解为什么即使是不可变整数也不是线程安全时显现出来。
让我们通过一个例子来看看。
假设我们有一个值为int count = 5
的变量,它被两个名为T1
和T2
的不同线程共享,两个线程同时读取和写入。
我们知道,如果将任何值重新分配给不可变对象,则旧对象仍然留在池中,新对象接管。
现在,当T1和T2将其值更新到计数变量中时,Java可能会将此值带入某些缓存中,并在那里执行设置操作,我们不知道JVM何时将更新后的值写入主内存,因此可能存在一个线程将值更新为完全过时的值的可能性。
这就引出了volatile关键字。
volatile - 此关键字确保所有对任何变量的I/O操作都将在主内存上进行,以便所有线程都使用最新的值。
考虑一下,如果有1个线程在写入,而所有其他线程都在读取,则volatile将解决我们的问题,但是如果所有线程都在同时读写同一个变量,则需要同步以确保线程安全。 volatile关键字不能确保线程安全。 现在,让我们来看看为什么要使用AtomicIntegers。即使我们使用synchronized关键字来确保线程安全,计数变量的实际更新操作也将是一个三步过程。volatile
意味着顺序一致性,因此递增一个volatile就意味着进行了顺序一致性存储。在x86上,这意味着完整的屏障,与lock add
的成本大致相同。但是在ARMv8.0上,例如,AtomicInteger递增将需要一个ldaxr
/stlxr
重试循环,而volatile加载/ volatile存储只需要一个非循环的ldar
(顺序获取加载)/stlr
(释放存储)。 https://godbolt.org/z/57nfTqEcr 显示了C代码的编译等效性。更多指令并不总是意味着更慢;单独的原子加载和原子存储类似于普通的加载/存储。 - Peter Cordes
AtomicInteger
(还有其他选项),但不能使用Integer
。 - Andy TurnerAtomicInteger
。 - eduardogr