【问题标题】:How does AtomicInteger is thread-safeAtomicInteger 如何是线程安全的
【发布时间】:2019-10-06 13:14:15
【问题描述】:

我正在阅读一些关于 Java 中的原子变量的文档。 随处可见,AtomicInteger 应该是线程安全的。

根据我对原子整数的理解,它的工作原理是比较和交换算法。 当两个线程尝试在完全相同的时间递增相同的原子变量时,我无法理解这将如何工作。

假设我定义了AtomicInteger var = 1,它被两个线程Thread_1Thread_2 使用。当两个线程都尝试同时增加var T1 时会发生什么。 我知道这将是罕见的情况,但如果它发生了怎么办。在比较和交换中,它在单个原子操作中读取和更新变量,并检查内存中的值。那么如果在T1-1 时,var 的值为 5 并且Thread1Thread2 都将开始递增呢? 哪一个会失败?会是随机行为吗?或者我错过了一些非常基本的东西。

【问题讨论】:

  • “我知道这将是罕见的” 稀有完全取决于具体情况。与AtomicLong 相比,Java 8 中提供了LongAdder 提供了一种在多个线程中增加 long 的竞争较少的方法,这一点很常见。

标签: java multithreading atomicinteger


【解决方案1】:

比较和交换在 CPU 级别是原子的。

您可以使用比较和交换显式实现增量操作:

int value;
do { 
  value = var.get();
} while (!var.compareAndSwap(value, value + 1));

CPU 保证compareAndSwap 是原子的(会有一个本机实现)。

如果两个线程同时命中这个compareAndSwap,那么只有一个线程会“获胜”,接收到true 作为compareAndSwap 调用的结果,因此循环停止。

另一个线程将“失败”,并收到false 作为结果,因此将再次循环:它读取一个新值,然后再次尝试CAS。如果成功(因为没有其他线程同时尝试执行此操作,或者它“赢得”了另一个线程),则循环停止;否则,它会再次尝试。

【讨论】:

    【解决方案2】:

    这是典型的比赛条件情况。将此增量操作视为带有小门的小房间,其中只有一个人可以容纳并执行一些操作。两个设法进入这个房间的第一个线程将执行增量操作。然后当它完成时,第二个=“不太幸运”线程将完成它的工作。但重要的是,两个操作的结果是一致的,因为它们不会并行执行增量操作。

    【讨论】:

      【解决方案3】:

      那么如果在时间 T1-1,var 的值为 5,Thread1 和 Thread2 都将开始递增呢?

      其中一个将成功执行比较和交换与当前值5 和新值6,另一个将失败并再次尝试值6 和新值7。它们不能同时使用5 的当前值和6 的新值成功,它是在 CPU 级别处理的。

      即使它们在不同的内核上运行并且 CAS 操作同时执行,仍然有一个操作会失败,这实际上是随机的(可能不取决于 CPU 实现)。

      【讨论】:

      猜你喜欢
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      • 2016-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-26
      • 1970-01-01
      相关资源
      最近更新 更多