原子操作和多线程

时间:2013-05-24 07:03:03

标签: java multithreading variables synchronization atomicity

最近我正在阅读一个教程,因为我发现了一个声明说的话。

“Java语言规范保证读取或写入变量是一个原子操作(除非变量的类型为longdouble)。类型为long或{的操作变量{1}}只有在使用double关键字声明时才是原子的。“

volatileAtomicInteger提供原始的AtomicLonggetAndDecrement()getAndIncrement()等方法。

我对上述陈述感到困惑..请您澄清何时使用 getAndSet()AtomicInteger类。

6 个答案:

答案 0 :(得分:51)

执行a = 28aint)是原子操作。但是执行a++不是原子操作,因为它需要读取a的值,增量和对结果的写入。因此,如果您使用a++来实现线程安全计数器,您可以让两个线程同时读取该值(例如26),然后同时增加它并同时写入它,从而导致27个结果,而不是28。

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

答案 1 :(得分:8)

Atomic意味着操作完成后不会发生任何事情。例如。 AtomicInteger上的getAndDecrement()保证变量同时返回和递减。

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

答案 2 :(得分:4)

如果您需要读取变量并根据读取值编写结果,则需要AtomicInteger。例如,i++读取i(例如3)并写入i+1(例如4)。线程可能同时被中断,另外三个线程也会增加i。现在我们回来了,i实际上有值6,但我们的帖子仍会根据事先读取的内容写4

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

答案 3 :(得分:0)

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

volatile long num;
num = num+1

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

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

答案 4 :(得分:0)

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

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

在多线程环境中,两个线程可以原子方式访问此代码并更新i的值并使其处于一致状态。因此,处理这种情况通常会保护代码“if(i == 10)i ++;”与同步块。但是,AtomicInteger类提供了实现此类功能的API,而不使用速度较慢的同步块。 AtmoicLong API的情况也是如此

答案 5 :(得分:0)

当变异变量时,需要

操作的原子性。做int a = 10;是一个原子操作,但它不会给你带来问题。提供操作的问题通常是变异,例如a++a = a + 2;等等。

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

原子类型有助于解决此问题。在原子类型上使用incrementAndget()使得'读取,添加1然后将结果写回来并在上下文中读取新结果'单个原子操作以线程安全。

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