32

最近我正在阅读一个教程,因为我遇到了一个声明说..

“Java 语言规范保证读取或写入变量是原子操作(除非变量的类型为longor double)。类型为 or 的操作变量long只有double在使用volatile在使用关键字

AtomicInteger或者AtomicLong提供类似getAndDecrement(),getAndIncrement()并且getAndSet()是原子的方法。

我对上面的陈述有点困惑..您能否澄清一下何时使用 AtomicIntegerAtomicLong上课。

4

6 回答 6

59

a = 28a作为一个int)是一个原子操作。但是doinga++不是原子操作,因为它需要读取a 的值、递增和写入a 的结果。因此,如果您曾经a++实现线程安全计数器,您可以让两个线程同时读取该值(例如 26),然后同时递增和写入它,结果是 27,而不是 28 .

AtomicInteger 通过提供您列出的原子操作来解决此问题。在我的示例中,您将使用incrementAndGet()例如,这将保证最终值为 28 而不是 27。

于 2013-05-24T07:09:05.287 回答
12

原子意味着操作完成,之间没有发生任何事情的可能性。例如。AtomicInteger 上的 getAndDecrement() 保证变量同时返回和递减。

如果它不是原子操作,则值可能会递减(例如从 3 到 2),然后由另一个线程修改(例如,将其从 2 更改为 5),然后返回为 5。

于 2013-05-24T07:09:00.847 回答
4

如果需要读取变量并根据读取AtomicInteger的值写入结果,则需要一个。例如,读取(例如)和写入(例如)。一个线程可能同时中断,另外三个线程也会增加。现在我们回来了,实际上有值,但我们的线程仍然写入,基于它事先读取的内容。i++i3i+14ii64

AtomicInteger.getAndIncrement确保您不会被打断,因此始终正确递增。此外,结果总是被刷新到内存中,而非易失性i可能不会被刷新到内存中。在这种情况下,其他线程甚至可能看不到更改。

于 2013-05-24T07:14:44.040 回答
1

当你改变一个变量时,操作的原子性是必需的。做int a = 10;是一个原子操作,但它不会给你带来问题。问题给出的操作通常是变异的,比如a++ora = a + 2;等​​等。

Java 规范保证“读取”和“写入”是原子操作,而不是它们的组合。因此,按照规范,“读取、加 1 然后将结果写回”的操作不是原子的。此类操作称为复合操作,它们通常需要在我们的代码中使用它们是原子的。

原子类型有助于解决这个问题。在原子类型上使用 incrementAndget() 使“读取,加 1,然后将结果写回并读取新结果”成为线程安全上下文中的单个原子操作。

希望这可以帮助。顺便说一句,您应该阅读这篇(http://walivi.wordpress.com/2013/08/24/concurrency-in-java-a-beginners-introduction/)关于并发和线程基础的文章。它很好地解释了这些东西。

于 2013-12-02T11:09:15.133 回答
0

我认为这意味着长和双读操作是原子的,而写操作是原子的。但是读+写不是原子的。

volatile long num;
num = num+1

以上不是线程安全的。读和写是两个独立的操作。每一个都保证是原子的,但整个表达式不是。

为了使其线程安全,您需要使用 AtomicLong 并使用 getAndIncrement 函数。

于 2013-05-24T07:11:52.973 回答
0

您根据正在处理的数字范围的上限/下限使用 int 或 long。请不要将 long 的非原子行为与 AtomicLong 混合。无论您在上面写什么都是正确的,但您可能混合了这两个概念。AtomicXXX 在您进行“比较和设置”类型的操作时更有用。例如,即使 int 可以原子地修改/读取,以下代码在多线程环境中也是不正确的:

int i =10
..
..
..
if(i == 10) i++;

在多线程环境中,两个线程可以原子地访问此代码并更新 i 的值并使其处于一致状态。所以处理这种情况通常你保护代码“if(i == 10) i++;” 带同步块。然而 AtomicInteger 类提供了 API 来实现这些事情,而无需使用较慢的同步块。AtmoicLong API 也是如此

于 2013-05-24T07:12:01.363 回答