java-i++和++i从jvm层面解释

2020-12-07  本文已影响0人  倔强青铜弟中弟

代码如下:

public static void main(String arg[]) {
        int a  = 0;
        a++;
        System.out.println(a);

        int b = 0;
        ++b;
        System.out.println(b);

        int c = 0;
        System.out.println(b++);

        int d = 0;
        System.out.println(++d);
    }

查看编译后的class文件

查看方式:可以直接使用命令:javap -v main.class。或者使用idea扩展插件jclasslib。使用方法可以自己百度吧。我不经常查看class文件,是使用的是命令。下面是完整的输出结果

Classfile /E:/MyProject/kite-school/kite-school-security/target/classes/com/school/mainTest.class
  Last modified 2020年12月7日; size 854 bytes
  MD5 checksum 6687beca749c37871dca7cd1caba837c
  Compiled from "mainTest.java"
public class com.school.mainTest
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #4                          // com/school/mainTest
  super_class: #5                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 2
Constant pool:
   #1 = Methodref          #5.#31         // java/lang/Object."<init>":()V
   #2 = Fieldref           #32.#33        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #34.#35        // java/io/PrintStream.println:(I)V
   #4 = Class              #36            // com/school/mainTest
   #5 = Class              #37            // java/lang/Object
   #6 = Class              #38            // com/school/mainTest$1
   #7 = Utf8               InnerClasses
   #8 = Class              #39            // com/school/mainTest$ObjectClass
   #9 = Utf8               ObjectClass
  #10 = Class              #40            // com/school/mainTest$ClassTest
  #11 = Utf8               ClassTest
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lcom/school/mainTest;
  #19 = Utf8               main
  #20 = Utf8               ([Ljava/lang/String;)V
  #21 = Utf8               arg
  #22 = Utf8               [Ljava/lang/String;
  #23 = Utf8               a
  #24 = Utf8               I
  #25 = Utf8               b
  #26 = Utf8               c
  #27 = Utf8               d
  #28 = Utf8               MethodParameters
  #29 = Utf8               SourceFile
  #30 = Utf8               mainTest.java
  #31 = NameAndType        #12:#13        // "<init>":()V
  #32 = Class              #41            // java/lang/System
  #33 = NameAndType        #42:#43        // out:Ljava/io/PrintStream;
  #34 = Class              #44            // java/io/PrintStream
  #35 = NameAndType        #45:#46        // println:(I)V
  #36 = Utf8               com/school/mainTest
  #37 = Utf8               java/lang/Object
  #38 = Utf8               com/school/mainTest$1
  #39 = Utf8               com/school/mainTest$ObjectClass
  #40 = Utf8               com/school/mainTest$ClassTest
  #41 = Utf8               java/lang/System
  #42 = Utf8               out
  #43 = Utf8               Ljava/io/PrintStream;
  #44 = Utf8               java/io/PrintStream
  #45 = Utf8               println
  #46 = Utf8               (I)V
{
  public com.school.mainTest();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 17: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/school/mainTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=5, args_size=1
         0: iconst_0
         1: istore_1
         2: iinc          1, 1
         5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: iload_1
         9: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        12: iconst_0
        13: istore_2
        14: iinc          2, 1
        17: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        20: iload_2
        21: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        24: iconst_0
        25: istore_3
        26: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        29: iload_2
        30: iinc          2, 1
        33: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        36: iconst_0
        37: istore        4
        39: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        42: iinc          4, 1
        45: iload         4
        47: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        50: return
      LineNumberTable:
        line 33: 0
        line 34: 2
        line 35: 5
        line 37: 12
        line 38: 14
        line 39: 17
        line 41: 24
        line 42: 26
        line 44: 36
        line 45: 39
        line 46: 50
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      51     0   arg   [Ljava/lang/String;
            2      49     1     a   I
           14      37     2     b   I
           26      25     3     c   I
           39      12     4     d   I
    MethodParameters:
      Name                           Flags
      arg
}
SourceFile: "mainTest.java"
InnerClasses:
  static #6;                              // class com/school/mainTest$1
  static #11= #10 of #4;                  // ClassTest=class com/school/mainTest$ClassTest of class com/school/mainTest

重点:

%DHAY97J{)VT54QE4GA2N5W.png

可以看到i++和++i的不同主要在于对操作数栈和局部变量表的操作顺序不同,导致当操作i的时候,发现i的值有所不同。这里大家可以参考下面的命令解释,然后再仔细看看我的截图,就会有很好的了解。更多的 指令集见:java class源文件指令集解释

使用命令解释

D5982X30(X7H2$Q{2(2~%BT.jpg

i++和++i线程安全问题

i++和++i的线程安全分为两种情况:
1、如果i是局部变量(在方法里定义的),那么是线程安全的。因为局部变量是线程私有的,别的线程访问不到,其实也可以说没有线程安不安全之说,因为别的线程对他造不成影响。
2、如果i是全局变量(类的成员变量),那么是线程不安全的。因为如果是全局变量的话,同一进程中的不同线程都有可能访问到。

可以使用Synchronized锁或者Lock接口的实现类锁,都可以

如果有大量线程同时执行i++操作,i变量的副本拷贝到每个线程的线程栈,当同时有两个线程栈以上的线程读取线程变量,假如此时是1的话,那么同时执行i++操作,再写入到全局变量,最后两个线程执行完,i会等于3而不会是2,所以,出现不安全性。

上一篇 下一篇

猜你喜欢

热点阅读