最近在工作中和一个同事因为自增是不是原子性操作争论的面红耳赤
那Java的自增操作到底是不是原子性操作呢
答案是否的
即Java的自增操作不是原子性操作
首先我们先看看Bruce Eckel是怎么说的
In the JVM an increment is not atomic and involves both a read and a write (via the latest Java Performance Tuning Newsletter)
意思很简单就是说在jvm中自增不是原子性操作它包含一个读操作和一个写操作
以上可能还不能让你信服要想让人心服口服就必须用代码说话正如FaceBook的文化一样代码赢得争论那我们就看一段代码
以下的代码是用个线程同时执行自增操作每个线程自增次如果自增操作是原子性操作的话那么执行完amount的值为运行代码之后你会发现amount的值小于这就说明自增操作不是原子性的
/**
*
* @author renrunwu
*/
public class MultiThread implements Runnable {
private int count;
private int amount = ;
public MultiThread() {
count = ;
}
public MultiThread(int count) {
unt = count;
}
@Override
public void run() {
for (int i = ; i < count; i++) {
amount++;
}
}
public static void main(String[] args) {
ExecutorService executorService = ExecutorsnewCachedThreadPool()
MultiThread multiThread =new MultiThread()
for (int i = ; i < ; i++) {
executorServiceexecute(multiThread)
}
executorServiceshutdown()
try {
Threadsleep()
} catch (InterruptedException e) {
eprintStackTrace()
}
Systemoutprintln(multiThreadamount)
}
}
如果以上还不能让你信服的话也没关系我们就把自增操作反编译出来看看java字节码是怎么操作的
以下是一个简单的自增操作代码
public class Increment {
private int id = ;
public void getNext(){
id++;
}
}
我们看看反编译之后的Java字节码主要关注getNext()方法内部的Java字节码
public class Increment extends javalangObject{
public Increment()
Code:
: aload_
: invokespecial #; //Method java/lang/Object<init>:()V
: aload_
: iconst_
: putfield #; //Field id:I
: return
public void getNext()
Code:
: aload_ //加载局部变量表index为的变量在这里是this
: dup //将当前栈顶的对象引用复制一份
: getfield #; //Field id:I获取id的值并将其值压入栈顶
: iconst_ //将int型的值压入栈顶
: iadd //将栈顶两个int类型的元素相加并将其值压入栈顶
: putfield #; //Field id:I将栈顶的值赋值给id
: return
}
很明显我们能够看到在getNext()方法内部对于类变量id有一个先取值后加一再赋值的过程因此我们可以很肯定的说Java中的自增操作不是原子性的
也许你会问那局部变量的自增操作是否是原子性的好我们在看看一下代码
public class Increment {
public void getNext(){
int id = ;
id++;
}
}
我们再看看反编译之后的Java字节码主要还是关注getNext()方法内部的Java字节码
public class Increment extends javalangObject{
public Increment()
Code:
: aload_
: invokespecial #; //Method java/lang/Object<init>:()V
: return
public void getNext()
Code:
: iconst_
: istore_
: iinc
: return
}
与全局变量的自增操作相比很明显局部变量的自增操作少了getfield与putfield操作而且对于局部变量来说它无论如何都不会涉及到多线程的操作因此局部变量的自增操作是否是原子操作也就显得不那么重要了