java原子操作
将以下语句分开是正确的:java原子操作,java,multithreading,atomic,Java,Multithreading,Atomic,将以下语句分开是正确的: int v = ++j; 作为: 读取j值(原子) 将读取的值增加1(非 原子干涉 其他线程) 将加法结果写入i (原子的) 将i写入v(原子) 我会用 带有的快速视图显示以下说明字节码: int j = 0; // 0 0:iconst_0 // 1 1:istore_1 int v = ++j; // 2 2:iinc 1 1 // 3 5:iload_1 // 4
int v = ++j;
作为:
int j = 0;
// 0 0:iconst_0
// 1 1:istore_1
int v = ++j;
// 2 2:iinc 1 1
// 3 5:iload_1
// 4 6:istore_2
接近。第二步是原子的。在这种情况下,
j
必须是byte
、char
、short
或int
中的一个,并且可以自动加载和存储其中的每一个
一旦一个值被加载到硬件寄存器中,另一个线程就不可能干扰它。JLS中可能有一些关于原始操作的原子性。。。但是我看不出来。是的,int
(或更小的数据类型)读/写/算术运算是原子的。引用(读/写)也是原子的,不管它是32位还是64位
但是,对64位long
和double
的操作可能不是原子操作
一些实现可能会发现将64位长
或双
值上的单个写入操作划分为相邻32位值上的两个写入操作很方便。为了提高效率,这种行为是特定于实现的;Java虚拟机可以对long
和double
值执行原子写入或分两部分写入
对于Java编程语言内存模型,对非易失性长
或双值的单次写入被视为两次单独的写入:每32位的一半写入一次。这可能导致线程在一次写入中看到64位值的前32位,在另一次写入中看到第二个32位。写入和读取volatile long
和double
值始终是原子的。对引用的写入和读取始终是原子的,不管它们是作为32位还是64位值实现的
鼓励VM实现者尽可能避免拆分其64位值。鼓励程序员将共享的64位值声明为volatile
,或者正确地同步他们的程序,以避免可能的复杂性
请注意,无论是增量/减量之前还是之后的运算符本身都不是原子的,甚至在int
或byte
上也不是原子的:读/写/算术运算在明显不同的步骤中发生
另见
是的,你的假设是正确的。递增一个int会导致三个步骤(因此它不是原子的),分配它是另一个步骤。下面是生成的字节码:
..
istore_1
iinc 1, 1
iload_1
istore_2
..
这个问题的目的是什么?了解编译器生成的字节码是什么?要了解如何使此线程安全?只需知道非读/写操作是否为原子操作即可。查看操作列表:;对于内存模型来说,数字相加是否是原子的并不重要。重要的是读/写。Java语言没有读-修改-写作为一个原子操作。还要注意,
v
不是易变的,因此不能保证其他读取线程会看到对它的写入。是的,谢谢,但我只想知道JVM是否像我写的那样分割上述操作。或者double
。(而不是volatile
)在示例代码中,它不能是long
或double
。步骤2是原子的吗?为什么?在许多书中,我读到只有读/写操作是原子的@xdevel2000回顾(当前)问题步骤1和3可能会使用不同的数据类型。@xdevel2000:在步骤2中,加载的值是递增的。它是原子的(或者可能这里不适用原子这个术语),因为加载的值是线程的本地值,所以没有其他人可以修改它。(但是另一个线程可能在第一个线程执行写操作之前执行读操作,因此增量可能仍然会被跳过)另请参见JLS保证防止单词撕裂谢谢,但是我真的不明白步骤2是否是原子的。斯蒂芬说“是”,而你和其他人似乎说“不是”。我很困惑。我的be i++作为一个整体被认为不是原子的,而所有被划分的子操作都是原子的(包括增量)??@xdevel2000:真正重要的是读写的原子性<代码>i++可能由原子读取和原子写入组成,但作为一个整体,不是一个原子“读-加-写”操作。
..
istore_1
iinc 1, 1
iload_1
istore_2
..