關(guān)于Java自增操作的原子性
最近在工作中和一個同事因?yàn)樽栽鍪遣皇窃有圆僮鳡幷摰拿婕t耳赤,那Java的自增操作到底是不是原子性操作呢,答案是否的,即Java的自增操作不是原子性操作。
1.首先我們先看看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中自增不是原子性操作,它包含一個讀操作和一個寫操作。
2.以上可能還不能讓你信服,要想讓人心服口服,就必須用代碼說話。正如FaceBook的文化一樣:代碼贏得爭論。那我們就看一段代碼:
以下的代碼是用100個線程同時(shí)執(zhí)行自增操作,每個線程自增100次,如果自增操作是原子性操作的話,那么執(zhí)行完amount的值為10,000。運(yùn)行代碼之后,你會發(fā)現(xiàn)amount的值小于10,000,這就說明自增操作不是原子性的
- /**
- *
- * @author renrun.wu
- */
- public class MultiThread implements Runnable {
- private int count;
- private int amount = 1;
- public MultiThread() {
- count = 100;
- }
- public MultiThread(int count) {
- this.count = count;
- }
- @Override
- public void run() {
- for (int i = 0; i < count; i++) {
- amount++;
- }
- }
- public static void main(String[] args) {
- ExecutorService executorService = Executors.newCachedThreadPool();
- MultiThread multiThread =new MultiThread();
- for (int i = 0; i < 100; i++) {
- executorService.execute(multiThread);
- }
- executorService.shutdown();
- try {
- Thread.sleep(60000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(multiThread.amount);
- }
- }
3.如果以上還不能讓你信服的話,也沒關(guān)系。我們就把自增操作反編譯出來,看看java字節(jié)碼是怎么操作的
以下是一個簡單的自增操作代碼
- public class Increment {
- private int id = 0;
- public void getNext(){
- id++;
- }
- }
我們看看反編譯之后的Java字節(jié)碼,主要關(guān)注getNext()方法內(nèi)部的Java字節(jié)碼。
- public class Increment extends java.lang.Object{
- public Increment();
- Code:
- : aload_0
- : invokespecial #1; //Method java/lang/Object."<init>":()V
- : aload_0
- : iconst_0
- : putfield #2; //Field id:I
- : return
- public void getNext();
- Code:
- : aload_0 //加載局部變量表index為0的變量,在這里是this
- : dup //將當(dāng)前棧頂?shù)膶ο笠脧?fù)制一份
- : getfield #2; //Field id:I,獲取id的值,并將其值壓入棧頂
- : iconst_1 //將int型的值1壓入棧頂
- : iadd //將棧頂兩個int類型的元素相加,并將其值壓入棧頂
- : putfield #2; //Field id:I,將棧頂?shù)闹蒂x值給id
- : return
- }
很明顯,我們能夠看到在getNext()方法內(nèi)部,對于類變量id有一個先取值后加一再賦值的過程。因此,我們可以很肯定的說Java中的自增操作不是原子性的。
4.也許你會問,那局部變量的自增操作是否是原子性的。好,我們在看看一下代碼:
- public class Increment {
- public void getNext(){
- int id = 0;
- id++;
- }
- }
我們再看看反編譯之后的Java字節(jié)碼,主要還是關(guān)注getNext()方法內(nèi)部的Java字節(jié)碼。
- public class Increment extends java.lang.Object{
- public Increment();
- Code:
- : aload_0
- : invokespecial #1; //Method java/lang/Object."<init>":()V
- : return
- public void getNext();
- Code:
- : iconst_0
- : istore_1
- : iinc 1, 1
- : return
- }
與全局變量的自增操作相比,很明顯局部變量的自增操作少了getfield與putfield操作。而且對于局部變量來說,它無論如何都不會涉及到多線程的操作,因此局部變量的自增操作是否是原子操作也就顯得不那么重要了。
原文鏈接:http://www.cnblogs.com/kevinwu/archive/2012/05/02/2479464.html