都说i++是线程不安全的,而且还说是先执行完全部操作后,在执行自增操作,那么这都是为什么呢,死记硬背是没有用的,就让我们从字节码指令的方面来了解这一切吧
首先介绍一个指令 javap -c
我们可以在后面接class文件名,如
既可以看到字节码指令了
如果我们想要把输入内容保存下来,可以在后面拼接 > 文件名或地址加文件名
i++
先粘代码
public class IaddTest {
public static void main(String[] args) {
int i = 0;
i = i++;
}
}
另一个字节码指令的代码
public class test.IaddTest {
public test.IaddTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: return
}
对照着字节码指令,我们就可以懂得每一步的意义了。
我们先来解释一段代码
0: iconst_0 // 0(int)值入栈。
1: istore_1 //将栈顶int类型值保存到局部变量1中。
2: iload_1 //从局部变量1中装载int类型值入栈。
3: iinc 1, 1 //将整数值1加到int类型的局部变量1中。
6: istore_1 //将栈顶int类型值保存到局部变量1中。 (这里请注意,是栈顶数据)
7: return // 返回局部变量1
从这些字节码指令的执行可以看出来,我们在执行iinc 1,1 这步的时候,局部变量i的值,已经+1了,但是,当执行到istore_1的时候,又把栈顶数据重新返回给了局部变量i,所以才导致了数据还是为0的现象的产生。
所以,我们可以把i++看成四步操作
int temp = i;
i = i + 1;
temp = i;
return temp;
所以,是不是发现,文章开头说的等所有执行完毕,再去自增,反而对不上了,所以呢,啥事都要自己去验证。skr
既然i++聊完了,顺便也就聊聊++i吧
++i
照例,粘代码
public class IaddTest {
public static void main(String[] args) {
int i = 0;
i = ++i;
}
}
字节码指令
public class test.IaddTest {
public test.IaddTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: iload_1
6: istore_1
7: return
}
对比一下i++的代码,就会发现先执行iinc 1,1 ,这个时候,局部变量i的值,已经+1了,然后我们把数据放入栈中,在取出来,并没有遭受过覆盖的遭遇。