Java中变量的读取和写入的原子性

时间:2017-03-10 17:00:21

标签: java multithreading atomicity

这是对另一个question of mine的跟进。

@templatetypedef回答了问题(赞赏),并在他的回答中写道:

  

作为注释 - 原子性并不意味着"所有其他线程都将被阻止   直到值准备好。这意味着所有其他线程都会看到   国家纯粹在手术完成之前或纯粹在手术之后   操作完成了,但没有别的。

我对此感到困惑,原因如下:

它说here

  

原子动作不能交错,因此可以毫无顾虑地使用它们   线程干扰。

我从中推断,这与他写的内容相矛盾。

如果我们有2个int变量i1和i2,并且我们执行原子操作i1=i2;并且此操作由threadX执行。

然后,如果原子动作不能如上所述交错,则意味着在此原子操作期间(由threadX执行),不允许其他threadY访问(用于读取或写入)相同的变量i2,因此,没有其他threadY,允许在原子操作期间访问同一个变量,因此存在某种形式的阻塞。

我做对了吗?

...谢谢

2 个答案:

答案 0 :(得分:3)

据我所知,没有原子i1 = i2操作。你可以原子方式读取一个int,你可以原子地写入一个,但你不能在同步操作中同时执行这两个操作。所以i1 = i2是两个不同的原子操作,一个读后跟一个写。您可以确保没有任何内容可以交错读取操作,因此您在阅读时不会看到i2的部分更新,并且您可以保证不会将写入交错到i1,但不能保证在这两个原子操作之间不会发生任何事情。

让我们说线程t1将会这样做:

i2 = 10
i1 = i2

线程t2将会:

i1 = 7
i2 = 18
System.out.println(i1)

您可以保证的是,t1最终会将10或18分配给i1,但您无法知道哪一个。但是,你可以保证它不能是任何其他值,因为读取i2和写入i1是原子的,所以你不能在修改它时看到i2的一些位。同样,t2保证打印10,18或7,它不能打印任何其他内容。但是,如果没有同步,就无法知道它最终会打印出哪3个值。

答案 1 :(得分:1)

  

...这意味着在此原子操作期间(由threadX执行),不允许其他threadY访问(对于读取或写入)相同的变量i2,因此,不允许其他threadY访问该变量在原子操作期间变量,因此存在某种形式的阻塞。

不,你没有做对。

原子操作意味着线程无法看到部分状态中的值。分配是原子的,具体取决于运行JVM的基础架构以及i1i2的数据大小。我相信Java说int字段是原子分配的,但long(和double)可能不是,因为它可能需要CPU进行多次操作。

  

原子动作不能交错,因此可以使用它们而不用担心线程干扰。

这是对的。如果i1为1且i2为2且threadX执行分配,则任何其他线程都会将i1的值视为1(旧值)或2 (新的价值)。 ThreadY不会在1或2之间看到某种中途,因为即使多个线程正在更新i1的值,该分配也是原子的。

但令人困惑的是,这里有两个概念:原子性和内存同步。对于线程,每个CPU都有自己的内存缓存,因此首先对本地内存进行内存操作,然后将这些更改写入主内存。即使另一个线程已经更新了主内存,线程也可能在其本地缓存内存中看到i1的旧副本。更糟糕的是,当两个线程更新了本地内存中i1的值并且根据它们的操作顺序(高度随机)时,一个线程的值将覆盖另一个线程对主内存的写入。很难知道哪一个会赢得race condition

  

作为注释 - 原子性并不意味着“在值准备好之前,所有其他线程都将被阻塞。

右。这是试图让你知道这里根本没有锁定。 ThreadY将看到的价值无法保证。 ThreadY也可以在同一时间更新i1到值3,然后其他线程可以将其视为1,2或3,具体取决于操作顺序以及这些线程是否交叉执行缓存刷新和更新时memory barriers

我们控制线程之间共享的字段和对象的方式是synchronized关键字,它为线程提供对资源的唯一访问权限。还有Lock和其他提供互斥的机制。我们还可以通过向字段添加volatile关键字来强制执行内存屏障,这意味着对字段的任何读取或写入都将对主内存进行。 synchronizedvolatile都可以确保正确发布数据和操作顺序。