从JVM 编译后的指令解释:为什么 i++ 不是原子操作?

来看一个例子:在这个例子中,我们定义了 increase() 方法,方法体是 num++ 的操作,我们通过编译成字节码文件中的jvm指令来看为什么 num++ 不是原子操作,而是包括读取变量的原始值、进行加1操作、写入工作内存。

 

1. 源文件 Test_volatile.java

 

public class Test_volatile {

    private volatile int num = 0;

    public void increase() {
        num++;
    }

    public static void main(String[] args) throws InterruptedException {
        final Test_volatile test = new Test_volatile();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++){
                        test.increase();
                    }
                };
            }.start();
        }
        // 睡眠 3s 保证 10个线程的自增操作全部执行完毕
        Thread.sleep(3000);
        System.out.println("num 的值为: " + test.num);
    }
}

2. 编译后的Test_volatile.class文件

cmd查看编译后的文件的命令:javap -c Test_volatile

 

Compiled from "Test_volatile.java"
public class com.practice.concurrent.keyWord_volatile.Test_volatile {
  public com.practice.concurrent.keyWord_volatile.Test_volatile();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_0
       6: putfield      #2                  // Field num:I
       9: return

  public void increase();
    Code:
       0: aload_0
       1: dup
       2: getfield      #2                  // Field num:I
       5: iconst_1
       6: iadd
       7: putfield      #2                  // Field num:I
      10: return

  public static void main(java.lang.String[]) throws java.lang.InterruptedException;
    Code:
       0: new           #3                  // class com/practice/concurrent/keyWord_volatile/Test_volatile
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: astore_1
       8: iconst_0
       9: istore_2
      10: iload_2
      11: bipush        10
      13: if_icmpge     33
      16: new           #5                  // class com/practice/concurrent/keyWord_volatile/Test_volatile$1
      19: dup
      20: aload_1
      21: invokespecial #6                  // Method com/practice/concurrent/keyWord_volatile/Test_volatile$1."<init>":(Lcom/practice/concurrent/keyWord_volatile/Test_volatile;)V
      24: invokevirtual #7                  // Method com/practice/concurrent/keyWord_volatile/Test_volatile$1.start:()V
      27: iinc          2, 1
      30: goto          10
      33: ldc2_w        #8                  // long 3000l
      36: invokestatic  #10                 // Method java/lang/Thread.sleep:(J)V
      39: getstatic     #11                 // Field java/lang/System.out:Ljava/io/PrintStream;
      42: new           #12                 // class java/lang/StringBuilder
      45: dup
      46: invokespecial #13                 // Method java/lang/StringBuilder."<init>":()V
      49: ldc           #14                 // String num 的值为:
      51: invokevirtual #15                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      54: aload_1
      55: getfield      #2                  // Field num:I
      58: invokevirtual #16                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      61: invokevirtual #17                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      64: invokevirtual #18                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      67: return
}

从编译后的结果来看, increase() 方法被编译为:

public void increase();
    Code:
       0: aload_0
       1: dup
       2: getfield      #2                  // Field num:I
       5: iconst_1
       6: iadd
       7: putfield      #2                  // Field num:I
      10: return

其中,这里有重要的几步,我加了注释进行说明:

 2: getfield      #2                  // Field num:I           // 这一步是从读取变量的原始值
 5: iconst_1                                                          // 这一步是 int型常量值1进栈,说白了就是把整数1准备好
 6: iadd                                                                // 这一步是加1的操作
 7: putfield      #2                  // Field num:I          // 这一步是写入工作内存

感谢大家的阅读!到这里终于明白为什么 num++ 不是原子操作了,如果对Java文件编译之后的jvm指令感兴趣,大家可以去详细了解一下JVM指令集。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值