繁体   English   中英

原子操作和多线程

[英]Atomic Operations and multithreading

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

“Java语言规范保证了读取或写入的变量是一个原子操作(除非该变量的类型的longdouble )类型的操作变量longdouble只有原子的,如果他们与申报volatile关键字。”

AtomicIntegerAtomicLong提供getAndDecrement()getAndIncrement()getAndSet()等原子方法。

我对上述陈述感到困惑。你能否澄清何时使用 AtomicIntegerAtomicLong类。

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

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

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

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

如果需要读取变量并根据读取值写入结果,则需要AtomicInteger 例如, i++读取i (例如3 )并写入i+1 (例如4 )。 一个线程可以同时中断,和其他三个线程递增i了。 现在我们回来了, i实际上有值6但我们的线程仍然根据它事先读取的内容写入4

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

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

volatile long num;
num = num+1

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

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

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

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

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

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

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

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

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

暂无
暂无

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

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