最近在中和一个同事因为自增是不是原子性操作争论的面红耳赤,那的自增操作到底是不是原子性操作呢,答案是否的,即Java的自增操作不是原子性操作。51Testing软件测试网8a7st"`|^w2Dk
1、首先我们先看看Bruce Eckel是怎么说的:51Testing软件测试网'ct7W4n6Q2QN51Testing软件测试网T$p{Ks4g S
In the JVM an increment is not atomic and involves both a read and a
write. (via the latest Java Performance Tuning Newsletter).G-{Hl[b+Kz051Testing软件测试网woQ%G-e4f
意思很简单,就是说在jvm中自增不是原子性操作,它包含一个读操作和一个写操作。51Testing软件测试网2TF(VsPtK51Testing软件测试网`R)Z'R*B
2、以上可能还不能让你信服,要想让人心服口服,就必须用代码说话。正如FaceBook的文化一样:代码赢得争论。那我们就看一段代码:/Y"~{0J:e]%r$@0
{,B$Y1Y,Ybg*T0以下的代码是用100个线程同时执行自增操作,每个线程自增100次,如果自增操作是原子性操作的话,那么执行完amount的值为10,000。运行代码之后,你会发现amount的值小于10,000,这就说明自增操作不是原子性的"N&t^.l-x)M`6eZ0/**
*
* @author renrun.wu
*/
publicclassMultiThreadimplementsRunnable {
privateintcount;
privateintamount =1;
publicMultiThread() {
count =100;
}
publicMultiThread(intcount) {
this.count = count;
}
@Override
publicvoidrun() {
for(inti =0; i
amount++;
}
}
publicstaticvoidmain(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
MultiThread multiThread =newMultiThread();
for(inti =0; i <100; i++) {
executorService.execute(multiThread);
}
executorService.shutdown();
try{
Thread.sleep(60000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(multiThread.amount);
}
}51Testing软件测试网aX5mL!W&ng0W
3、如果以上还不能让你信服的话,也没关系。我们就把自增操作反编译出来,看看java字节码是怎么操作的's O&jpUz o2Czc@0
c,|-{H$o.BnB0以下是一个简单的自增操作代码fIo9g2W"o4QV3|*q0publicclassIncrement {
privateintid =0;
FUlU*h@"Nv$`0
publicvoidgetNext(){
id++;
}
}
我们看看反编译之后的Java字节码,主要关注getNext()方法内部的Java字节码。51Testing软件测试网@,JsbhL
51Testing软件测试网1h+j^!^1u%PGY
{hpublicclassIncrementextendsjava.lang.Object{
publicIncrement();
Code:
: aload_0
: invokespecial #1;//Method java/lang/Object."":()V
: aload_0
: iconst_0
: putfield #2;//Field id:I
:return
publicvoidgetNext();
Code:
: aload_0//加载局部变量表index为0的变量,在这里是this
: dup//将当前栈顶的对象引用复制一份
: getfield #2;//Field id:I,获取id的值,并将其值压入栈顶
: iconst_1//将int型的值1压入栈顶
: iadd//将栈顶两个int类型的元素相加,并将其值压入栈顶
: putfield #2;//Field id:I,将栈顶的值赋值给id
:return
}51Testing软件测试网_U0GC:v
很明显,我们能够看到在getNext()方法内部,对于类变量id有一个先取值后加一再赋值的过程。因此,我们可以很肯定的说Java中的自增操作不是原子性的。&T5m-d3F2Y051Testing软件测试网Gs!n{d/v|$z#ZA
4、也许你会问,那局部变量的自增操作是否是原子性的。好,我们在看看一下代码:VP1|H]t1C6r[MB0
zNSXvE6v051Testing软件测试网
w+?.v`~'XrDZ)Z+W WpublicclassIncrement {
publicvoidgetNext(){
intid =0;
id++;
}
}51Testing软件测试网BJ,f^
L4q8k
我们再看看反编译之后的Java字节码,主要还是关注getNext()方法内部的Java字节码。51Testing软件测试网6te] J-R
up
e1f0K'x$zGxh,B051Testing软件测试网%VO,c$zd/er&hpublicclassIncrementextendsjava.lang.Object{
publicIncrement();
Code:
: aload_0
: invokespecial #1;//Method java/lang/Object."":()V
:return
publicvoidgetNext();
Code:
: iconst_0
: istore_1
: iinc1,1
:return
}
:ub`+?@'JB2j-w0与全局变量的自增操作相比,很明显局部变量的自增操作少了getfield与putfield操作。而且对于局部变量来说,它无论如何都不会涉及到多线程的操作,因此局部变量的自增操作是否是原子操作也就显得不那么重要了。)f;h3M;dSS0