Java的自增操作与原子性操作的区别之处

    作者:课课家教育更新于: 2016-03-11 16:42:33

    大神带你学编程,欢迎选课

      最近因为自增是不是原子性操作争论的面红耳赤,那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个线程同时执行自增操作,每个线程自增100次,如果自增操作是原子性操作的话,那么执行完amount的值为10,000。运行代码之后,你会发现amount的值小于10,000,这就说明自增操作不是原子性的

    Java的自增操作不原则操作的区别_Java自操作_课课家

        /**  
         *   
         * @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.如果以上还不能让你信服的话,也没关系。我们就把自增操作反编译出来,看看java字节码是怎么操作的

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

        public class Increment {  
            private int id = 0;  
         
            public void getNext(){  
                id++;  
            }  
        } 
    

      我们看看反编译之后的Java字节码,主要关注getNext()方法内部的Java字节码。

        public class Increment extends java.lang.Object{  
            public Increment();  
              Code:  
        :   aload_0  
        :   invokespecial   #1; //Method java/lang/Object."":()V  
        :   aload_0  
        :   iconst_0  
        :   putfield        #2; //Field id:I  
        :   return 
         
            public void getNext();  
              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 
         
            } 
    

      很明显,我们能够看到在getNext()方法内部,对于类变量id有一个先取值后加一再赋值的过程。因此,我们可以很肯定的说Java中的自增操作不是原子性的。

      4.也许你会问,那局部变量的自增操作是否是原子性的。在看看一下代码:

        public class Increment {  
            public void getNext(){  
            int id = 0;  
                id++;  
            }  
        } 
    

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

        public class Increment extends java.lang.Object{  
        public Increment();  
          Code:  
        :   aload_0  
        :   invokespecial   #1; //Method java/lang/Object."":()V  
        :   return 
         
        public void getNext();  
          Code:  
        :   iconst_0  
        :   istore_1  
        :   iinc    1, 1 
        :   return 
         
        } 
    

      与全局变量的自增操作相比,很明显局部变量的自增操作少了getfield与putfield操作。而且对于局部变量来说,它无论如何都不会涉及到多线程的操作,因此局部变量的自增操作是否是原子操作也就显得不那么重要了。

课课家教育

未登录