关于Java自增操作的原子性

上一篇 / 下一篇  2012-05-25 09:12:43 / 个人分类:Java

最近在工作中和一个同事因为自增是不是原子性操作争论的面红耳赤,那Java的自增操作到底是不是原子性操作呢,答案是否的,即Java的自增操作不是原子性操作。51Testing软件测试网-u9b&X0J u1rc*E

  1、首先我们先看看Bruce Eckel是怎么说的:

L7Lp+{3~m] a051Testing软件测试网/{r@ U2j5UG

   In the JVM an increment is not atomic and involves both a read and a write. (via the latest Java Performance Tuning Newsletter)

9_4PJ1H u)p.VQ"M6K0

4R*c EdK}y+KW^,t0  意思很简单,就是说在jvm中自增不是原子性操作,它包含一个读操作和一个写操作。51Testing软件测试网|BF dS

51Testing软件测试网x6rt3|o V#p)F9Hr

  2、以上可能还不能让你信服,要想让人心服口服,就必须用代码说话。正如FaceBook的文化一样:代码赢得争论。那我们就看一段代码:

K[,BF D&h5e`0

HU)g&u3[u8z.Oy N0  以下的代码是用100个线程同时执行自增操作,每个线程自增100次,如果自增操作是原子性操作的话,那么执行完amount的值为10,000。运行代码之后,你会发现amount的值小于10,000,这就说明自增操作不是原子性的

T#x+Y&u~el s0
  1. /** 
  2.  *  
  3.  * @author renrun.wu 
  4.  */ 
  5. public class MultiThread implements Runnable {  
  6.     private int count;  
  7.     private int amount = 1;  
  8.       
  9.     public MultiThread() {  
  10.          count = 100;  
  11.     }  
  12.       
  13.     public MultiThread(int count) {  
  14.         this.count = count;  
  15.     }  
  16.       
  17.     @Override 
  18.     public void run() {  
  19.         for (int i = 0; i < count; i++) {  
  20.             amount++;  
  21.         }  
  22.     }  
  23.       
  24.     public static void main(String[] args) {  
  25.         ExecutorService executorService = Executors.newCachedThreadPool();  
  26.         MultiThread multiThread =new MultiThread();  
  27.         for (int i = 0; i < 100; i++) {  
  28.             executorService.execute(multiThread);  
  29.         }  
  30.         executorService.shutdown();  
  31.           
  32.         try {  
  33.             Thread.sleep(60000);  
  34.         } catch (InterruptedException e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.         System.out.println(multiThread.amount);  
  38.     }  
  39. }
51Testing软件测试网3zmDQ+Y

  3、如果以上还不能让你信服的话,也没关系。我们就把自增操作反编译出来,看看java字节码是怎么操作的

:oh^ R S&xb051Testing软件测试网#Y/o/ISo

  以下是一个简单的自增操作代码

2E?4rb5^ m.S|0
  1. public class Increment {  
  2.     private int id = 0;  

  3. Pm PAOZ#Zl)T0
  4.     public void getNext(){  
  5.         id++;  
  6.     }  
  7. }
  8. 我们看看反编译之后的Java字节码,主要关注getNext()方法内部的Java字节码。

    ]Km aI_x+~/zl051Testing软件测试网xd;G7JTG t2yE

    1. public class Increment extends java.lang.Object{  
    2.     public Increment();  
    3.       Code:  
    4. :   aload_0  
    5. :   invokespecial   #1//Method java/lang/Object."<init>":()V 
    6. :   aload_0  
    7. :   iconst_0  
    8. :   putfield        #2//Field id:I 
    9. :   return 
    10.  
    11.     public void getNext();  
    12.       Code:  
    13. :   aload_0   //加载局部变量表index为0的变量,在这里是this  
    14. :   dup                 //将当前栈顶的对象引用复制一份 
    15. :   getfield        #2//Field id:I,获取id的值,并将其值压入栈顶 
    16. :   iconst_1            //将int型的值1压入栈顶 
    17. :   iadd                //将栈顶两个int类型的元素相加,并将其值压入栈顶 
    18. :   putfield        #2//Field id:I,将栈顶的值赋值给id 
    19. :  return 
    20.  
    21.     }

    oU$`z\ J*c%e7O0  很明显,我们能够看到在getNext()方法内部,对于类变量id有一个先取值后加一再赋值的过程。因此,我们可以很肯定的说Java中的自增操作不是原子性的。51Testing软件测试网$q%g1oKx

    'q |$ZA0`V+cm7[0  4、也许你会问,那局部变量的自增操作是否是原子性的。好,我们在看看一下代码:51Testing软件测试网Cs6Xw6\ Pp

    51Testing软件测试网*M1j$bo9oE/x

    z [&VSd|9O0V1R3U0
    1. public class Increment {  
    2.     public void getNext(){  
    3.     int id = 0;  
    4.         id++;  
    5.     }  
    6. }

    }i onv a i0  我们再看看反编译之后的Java字节码,主要还是关注getNext()方法内部的Java字节码。

    @cZTk~ D051Testing软件测试网T}3q`Sqs&xM

    51Testing软件测试网M g;BW!jo6B~^$g3I

    1. public class Increment extends java.lang.Object{  
    2. public Increment();  
    3.   Code:  
    4. :   aload_0  
    5. :   invokespecial   #1//Method java/lang/Object."<init>":()V 
    6. :   return 
    7.  
    8. public void getNext();  
    9.   Code:  
    10. :   iconst_0  
    11. :   istore_1  
    12. :   iinc    11 
    13. :   return 
    14.  
    15. }

    -KQU?J7PF y0  与全局变量的自增操作相比,很明显局部变量的自增操作少了getfield与putfield操作。而且对于局部变量来说,它无论如何都不会涉及到多线程的操作,因此局部变量的自增操作是否是原子操作也就显得不那么重要了。51Testing软件测试网*D-q+@9a!F$Jm [*`'U9^0_


TAG:

 

评分:0

我来说两句

Open Toolbar