1. 赋值操作-原子性
64位虚拟机的引用是64bit或32bit(开启指针压缩,堆内存无法超过32GB),因此一次赋值操作(=
)是原子的。
2. “可见性”
原子操作、和可见性经常被同时提及。
2.1 无需保证可见性的场景
但两者是完全不同的语义,并且大多数场景下,我们只需要保证update操作的原子性,而不需要保证严格的“及时”。
所谓严格的及时指 “可见性保证”。
修改者改变了某个值,但读取者无需立即知道新的值(也就是,在业务上使用稍过期的值并没有什么问题)。此时我们可以不使用volatile(也就是无需保证可见性)
2.2 需要保证可见性的场景
假设,我们有一个long[]数组, 我们通过Unsafe可以很轻易地做到 atomic + volatie
的修改其中一个long元素。
如果业务有如下要求, index为N的元素和 index为N-1的元素有关联,两者共同组成了一种业务语义。
并且这个long[]数组整体是共享的,那么我们必须要保证元素的可见性。
因为我们不可能拿新的index=N的元素和旧的index=N-1的元素共同表达业务语义。
这和32bit虚拟机无法保证 long类型的赋值操作是”原子性”的原因类似——后32bit和前32bit是一个整体,我们不应当看见一个 “牛头马尾”。而是一个完整的牛或马。
2.2.1 如何解决上述场景
转换一下思维,若 long1和long2组成一个整体,我们把它定义为一个新的ClassA。
将long[] 改为 A[],然后” 原子性” 且 “volatile” 的修改元素。
缺点是引入了一个新的Class,带来了额外开销。