最近在工作中和一個同事因為自增是不是原子性操作爭論的面紅耳赤
那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 renrun
wu
*/
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 = Executors
newCachedThreadPool()
MultiThread multiThread =new MultiThread()
for (int i =
; i <
; i++) {
executorService
execute(multiThread)
}
executorService
shutdown()
try {
Thread
sleep(
)
} catch (InterruptedException e) {
e
printStackTrace()
}
System
out
println(multiThread
amount)
}
}
如果以上還不能讓你信服的話
也沒關系
我們就把自增操作反編譯出來
看看java字節碼是怎麼操作的
以下是一個簡單的自增操作代碼
public class Increment {
private int id =
;
public void getNext(){
id++;
}
}
我們看看反編譯之後的Java字節碼
主要關注getNext()方法內部的Java字節碼
public class Increment extends java
lang
Object{
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 java
lang
Object{
public Increment()
Code:
: aload_
: invokespecial #
; //Method java/lang/Object
<init>
:()V
: return
public void getNext()
Code:
: iconst_
: istore_
: iinc
: return
}
與全局變量的自增操作相比
很明顯局部變量的自增操作少了getfield與putfield操作
而且對於局部變量來說
它無論如何都不會涉及到多線程的操作
因此局部變量的自增操作是否是原子操作也就顯得不那麼重要了
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26944.html