我知道i ++不是一个线程安全的操作。我也理解为什么i ++比i = i + 1更快。在线程安全方面,i = i + 1与i ++有什么不同吗?任何字节码级别的解释都会非常有用。
答案 0 :(得分:9)
i += 1
和i++
都不是原子的(也不是线程安全的)。 ++i
也是如此。这是一个简单的测试,您可以运行来证明这一点:
public class Test {
static volatile int x, y;
static class IncThread extends Thread {
public void run() {
for (int i=0; i<50000; i++) x++;
for (int i=0; i<50000; i++) y = y+1;
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new IncThread();
Thread t2 = new IncThread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.printf("x = %d, y = %d%n", x, y);
}
}
这是我得到的输出:
x = 99897, y = 81556
显然,有些写作丢失了。有一篇不错的小博文++ not considered atomic,可以解释这一点。那篇文章还指出@ August的回答是误导性的。该字节码(iinc
)仅用于递增局部变量,这从线程安全的角度来看并不令人感兴趣。 (该博客文章还讨论了用于增量的不同字节码。)
答案 1 :(得分:6)
i++
和i += 1
之间没有区别,字节码明智:
增量(来源):
public static void main(String[] args) {
int i = 0;
i++;
}
增量(字节码):
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: return
化合物添加(来源):
public static void main(String[] args) {
int i = 0;
i += 1;
}
化合物添加(字节码):
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: return
用于递增字段的字节码也是相同的,尽管它没有使用iinc
指令(因为它需要一个局部变量索引):
int x;
void inc() { x++; }
void assign() { x += 1; }
void inc();
Code:
0: aload_0
1: dup
2: getfield #2 // Field x:I
5: iconst_1
6: iadd
7: putfield #2 // Field x:I
10: return
void assign();
Code:
0: aload_0
1: dup
2: getfield #2 // Field x:I
5: iconst_1
6: iadd
7: putfield #2 // Field x:I
10: return
答案 2 :(得分:1)
i=i+1
使用二进制运算符(+)加载i的值,并向其中加1,然后将结果存储回i。相反,i++
使用一元(++)运算符,它只使用单个汇编指令递增值,因此理论上它可能更有效。但是,今天的编译器优化i=i+1
和i++
会产生相同的优化代码。
答案 3 :(得分:-1)
在这些术语中,i ++和i = i + 1之间没有差异。