繁体   English   中英

Java中的Atomic Integer和Normal immutable Integer类有什么区别?

[英]What is the difference between Atomic Integer and Normal immutable Integer class in Java?

由于 Integer 类也是不可变类,我们知道不可变类是线程安全的,这是 Atomic Integer 的需要。 我很困惑 。 是不是不可变对象的读写不需要是原子的,而原子整数的读写是原子的。 这意味着原子类也是线程安全的。

当您需要确保只有一个线程可以更新 int 变量时,AtomicInteger 用于多线程环境。 优点是不需要外部同步,因为修改它的值的操作是以线程安全的方式执行的。

考虑以下代码:

private int count;

public int updateCounter() {
   return ++count;
}

如果多个线程调用 updateCounter 方法,它们中的一些可能会收到相同的值。 ++count 操作不是原子操作的原因不仅仅是一个操作,而是由三个操作组成: read count, add 1 to it's value and write it back to it 多个调用线程可以看到该变量未修改为其最新值。

上面的代码应该替换为:

private AtomicInteger count = new AtomicInteger(0);
public int updateCounter() {
    return count.incrementAndGet();
}

incrementAndGet方法保证自动递增存储的值并返回它的值,而不使用任何外部同步。

如果你的值永远不会改变,你不必使用 AtomicInteger,使用 int 就足够了。

AtomicInteger 是线程安全的(事实上,java.util.concurrent.atomic 包中的所有类都是线程安全的),而普通整数不是线程安全的。

当您在多线程环境中使用“整数”变量(以使其线程安全)时,您将需要“同步”和“易失性”关键字,而对于原子整数,您不需要“同步”和“易失性”作为原子整数的关键字负责线程安全。

另外,我会推荐以下关于同一主题的有用教程:http: //tutorials.jenkov.com/java-concurrency/compare-and-swap.html

有关“原子”包的更多信息,请参阅下面的 oracle 文档: https ://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html

虽然根据定义,不可变对象是线程安全的,但可变对象也可以是线程安全的。

这正是Atomic...类( AtomicIntegerAtomicBoolean等)的目的。

各种...get......set...方法允许对象的线程安全访问和变异。

毫不奇怪,该类是在java.util.concurrent包中声明的。

您只需浏览java.util.concurrent.atomic包的API

一个小的类工具包,支持对单个变量进行无锁线程安全编程。

考虑一个变量

int myInt = 3;

AtomicIntegermyInt

Integer3相关。

换句话说,你的变量是可变的并且可以改变它的值。 而值 3 是一个整数文字,一个常量,一个不可变的表达式。

整数是文字的对象表示,因此是不可变的,您基本上只能读取它们。

AtomicIntegers 是这些值的容器。 您可以阅读和设置它们。 与为变量赋值相同。 但与更改int变量的值不同,对AtomicInteger的操作是原子的。

例如,这不是原子的

if(myInt == 3) {
   myInt++; 
}

这是原子的

AtomicInteger myInt = new AtomicInteger(3);

//atomic
myInt.compareAndSet(3, 4);

我认为 AtomicInteger 和普通不可变 Integer 之间的主要区别将会显现出来,一旦我们了解为什么即使是不可变 Integer 也不是线程安全的。

让我们看一个例子。

假设,我们有一个int count = 5的值,它被两个名为T1T2的不同线程共享,同时读取和写入。

我们知道,如果有任何值被重新分配给不可变对象,旧对象将保留在池中,而新对象将接管。

现在,当 T1 和 T2 将它们的值更新为 count 变量时,Java 可能会将这个值放入某个缓存中并在那里进行设置操作,我们不知道 JVM 何时会将更新后的值写入主内存,所以可能会有其中一个线程可能会将值更新为完全过时的值。

这将我们带到了volatile关键字。

Volatile - 这个关键字确保对任何变量的所有 I/O 操作都将在主内存上进行,以便所有线程都使用最新的值。

考虑一下,如果 1 个线程正在写入,而其他所有线程都在读取,那么 volatile 将解决我们的问题,但如果所有线程同时在同一个变量上读写,那么我们需要同步以确保线程安全。

volatile 关键字不能确保线程安全。

现在,为什么要使用 AtomicIntegers。 即使使用syncrhonized关键字来保证线程安全,count变量的实际更新操作也将是一个三步过程。

  1. 获取计数变量的更新值
  2. 将值增加 1
  3. 将值设置为计数变量

这就是为什么一旦考虑到线程安全,更新普通整数的任何值以更新值需要稍长的时间。

**AtomicIntegers 通过一种称为 Compare-And-Swap(CAS 方法)的优化无锁算法进一步解决了线程安全和更快更新的问题。

它们以单步过程原子方式执行所有更新操作。 **

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM