这是对另一个question of mine的跟进。
@templatetypedef回答了问题(赞赏),并在他的回答中写道:
作为注释 - 原子性并不意味着"所有其他线程都将被阻止 直到值准备好。这意味着所有其他线程都会看到 国家纯粹在手术完成之前或纯粹在手术之后 操作完成了,但没有别的。
我对此感到困惑,原因如下:
它说here:
原子动作不能交错,因此可以毫无顾虑地使用它们 线程干扰。
我从中推断,这与他写的内容相矛盾。
如果我们有2个int变量i1和i2,并且我们执行原子操作i1=i2;
并且此操作由threadX执行。
然后,如果原子动作不能如上所述交错,则意味着在此原子操作期间(由threadX执行),不允许其他threadY访问(用于读取或写入)相同的变量i2,因此,没有其他threadY,允许在原子操作期间访问同一个变量,因此存在某种形式的阻塞。
我做对了吗?
...谢谢
答案 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的基础架构以及i1
和i2
的数据大小。我相信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
关键字来强制执行内存屏障,这意味着对字段的任何读取或写入都将对主内存进行。 synchronized
和volatile
都可以确保正确发布数据和操作顺序。