相反,Fetch-and-Add操作对应着名的i增量操作,将是非原子的,因为导致了读 – 修改 – 写操作.
假设这个代码:
public void assign(int b) { int a = b; }
生成的字节码是:
public void assign(int); Code: 0: iload_1 1: istore_2 2: return
因此,我们看到分配由两个步骤组成(加载和存储).
假设这个代码:
public void assign(int b) { int i = b++; }
字节码:
public void assign(int); Code: 0: iload_1 1: iinc 1,1 //extra step here regarding the prevIoUs sample 4: istore_2 5: return
知道X86处理器可以(至少是现代的)可以按原样操作增量操作:
In computer science,the fetch-and-add cpu instruction is a special
instruction that atomically modifies the contents of a memory
location. It is used to implement mutual exclusion and concurrent
algorithms in multiprocessor systems,a generalization of semaphores.
因此,第一个问题:尽管字节码需要两个步骤(加载和存储),但是Java依赖于这样一个事实,即分配操作是始终以原子方式执行的操作,无论处理器的架构如何,因此可以确保永久原子性(用于原始分配)在其规格?
第二个问题:使用非常现代化的X86处理器来确认并且在不同体系结构之间没有共享编译代码是错误的,所以不需要同步i操作(或AtomicInteger)?考虑到它已经是原子的.
解决方法
你意味着我会翻译成X86 Fetch-And-Add指令,这是不正确的.如果代码由JVM进行编译和优化,则可能是真的(必须检查JVM的源代码来确认),但是该代码也可以在解释模式下运行,其中fetch和add被分离并且不同步.
public class Main { volatile int a; static public final void main (String[] args) throws Exception { new Main ().run (); } private void run () { for (int i = 0; i < 1000000; i++) { increase (); } } private void increase () { a++; } }
我使用Java HotSpot(TM)Server VM(17.0-b12-fastdebug)for windows-x86 JRE(1.6.0_20-ea-fastdebug-b02),建立于2010年4月1日03:25:33版本的JVM(这一个我在我的驱动器的某个地方).
这些是运行它的关键输出(java -server -XX:PrintAssembly -cp.Main):
起初它被编成这个:
00c PUSHL EBP SUB ESP,8 # Create frame 013 MOV EBX,[ECX + #8] # int ! Field VolatileMain.a 016 MEMBAR-acquire ! (empty encoding) 016 MEMBAR-release ! (empty encoding) 016 INC EBX 017 MOV [ECX + #8],EBX ! Field VolatileMain.a 01a MEMBAR-volatile (unnecessary so empty encoding) 01a LOCK ADDL [ESP + #0],0 ! membar_volatile 01f ADD ESP,8 # Destroy frame POPL EBP TEST PollPage,EAX ! Poll Safepoint 029 RET
然后它被内联和编译成这样:
0a8 B11: # B11 B12 <- B10 B11 Loop: B11-B11 inner stride: not constant post of N161 Freq: 0.999997 0a8 MOV EBX,[ESI] # int ! Field VolatileMain.a 0aa MEMBAR-acquire ! (empty encoding) 0aa MEMBAR-release ! (empty encoding) 0aa INC EDI 0ab INC EBX 0ac MOV [ESI],EBX ! Field VolatileMain.a 0ae MEMBAR-volatile (unnecessary so empty encoding) 0ae LOCK ADDL [ESP + #0],0 ! membar_volatile 0b3 CMP EDI,#1000000 0b9 Jl,s B11 # Loop end P=0.500000 C=126282.000000
你可以看到它不使用Fetch-And-Add指令.