3 回答

TA贡献2041条经验 获得超4个赞
i++
在Java中可能不是原子的,因为原子性是一个特殊的要求,在大多数用途中都不存在i++
。该要求具有显着的开销:使增量操作成为原子的成本很高; 它涉及在普通增量中不需要存在的软件和硬件级别的同步。
您可以将i++
应该设计和记录的参数设置为专门执行原子增量,以便使用执行非原子增量i = i + 1
。但是,这会破坏Java,C和C ++之间的“文化兼容性”。同样,它会删除一个方便的符号,熟悉类C语言的程序员认为这是理所当然的,赋予它一个特殊的含义,仅适用于有限的情况。
基本的C或C ++代码就像for (i = 0; i < LIMIT; i++)
转换为Java一样for (i = 0; i < LIMIT; i = i + 1)
; 因为使用原子是不合适的i++
。更糟糕的是,程序员从C语言或其他类C语言到Java都会使用i++
,导致不必要地使用原子指令。
即使在机器指令集级别,由于性能原因,增量类型操作通常也不是原子的。在x86中,必须使用特殊指令“lock prefix”来使inc
指令成为原子:出于与上述相同的原因。如果inc
总是原子的,那么当需要非原子公司时,它永远不会被使用; 程序员和编译器会生成加载,添加1和存储的代码,因为它会更快。
在一些指令集架构中,没有原子inc
或根本没有inc
; 要在MIPS上执行原子公司,你必须编写一个使用ll
and sc
:load-linked和store-conditional 的软件循环。加载链接读取单词,如果单词未更改,则store-conditional存储新值,否则失败(检测到并导致重新尝试)。

TA贡献1811条经验 获得超5个赞
i++ 涉及两个操作:
读取当前值 i
递增值并将其分配给 i
当两个线程i++同时对同一个变量执行时,它们可能都获得相同的当前值i,然后递增并设置为i+1,因此您将获得单个增量而不是两个。
示例:
int i = 5;
Thread 1 : i++;
// reads value 5
Thread 2 : i++;
// reads value 5
Thread 1 : // increments i to 6
Thread 2 : // increments i to 6
// i == 6 instead of 7

TA贡献1799条经验 获得超8个赞
为什么i ++在Java中不是原子的?
让我们将增量操作分解为多个语句:
线程1和2:
从内存中获取总值
将1添加到值
写回内存
如果没有同步,那么让我们说线程1读取值3并将其增加到4,但是没有写回来。此时,发生上下文切换。线程2读取值3,递增它并发生上下文切换。虽然两个线程都增加了总值,但仍然是4 - 竞争条件
添加回答
举报