zoukankan      html  css  js  c++  java
  • Java精通并发-锁粗化与锁消除技术实例演示与分析

    在上一次https://www.cnblogs.com/webor2006/p/11446473.html中对锁的升级进行了一个比较详细的理论化的学习,先回忆一下:

    编译器对于锁的优化措施: 

    锁消除技术:

    接下来则会通过实例来分析一下JIT编译器优化的一些方式,先来看第一个例子:

    很简单的程序,然后反编译看一下它在字节码的表现:

     接下来则来修改一下程序:

    其实反编译的字节码的锁还是会有的:

    但是很明显这段同步的意义就不大了,因为每个线程在访问这个方法时的局部变量肯定都是不一样的,不同的对象锁也不一样,那何来的同步,所以其实JIT在程序运行时是比较智能的,JIT编译器(Just In Time编译器)可以在动态编译同步代码时,使用一种叫做逃逸分析的技术,来通过该项技术判别程序中所使用的锁对象是否只被一个线程所使用,而没有散布到其他线程当中;如果情况就是这样的话,那么JIT编译器在编译这个同步代码时就不会生成synchronized关键字标识的锁的申请和释放机器码,从而消除了锁的使用流程。

    锁粗化:

    好,接下来看另外一个例子:

    根据上面的理论,很显然在运行是JIT是不会给代码上锁的,因为此object声明的是方法的局部变量,木啥意义,那如果将它改为成员变量呢?

    可见这个方法块中多次给代码上了锁,下面看一下它在字节码上的表现:

      public void method();
        Code:
           0: aload_0
           1: getfield      #3                  // Field object:Ljava/lang/Object;
           4: dup
           5: astore_1
           6: monitorenter
           7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
          10: ldc           #5                  // String hello world
          12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          15: aload_1
          16: monitorexit
          17: goto          25
          20: astore_2
          21: aload_1
          22: monitorexit
          23: aload_2
          24: athrow
          25: aload_0
          26: getfield      #3                  // Field object:Ljava/lang/Object;
          29: dup
          30: astore_1
          31: monitorenter
          32: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
          35: ldc           #7                  // String welcome
          37: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          40: aload_1
          41: monitorexit
          42: goto          50
          45: astore_3
          46: aload_1
          47: monitorexit
          48: aload_3
          49: athrow
          50: aload_0
          51: getfield      #3                  // Field object:Ljava/lang/Object;
          54: dup
          55: astore_1
          56: monitorenter
          57: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
          60: ldc           #8                  // String person
          62: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          65: aload_1
          66: monitorexit
          67: goto          77
          70: astore        4
          72: aload_1
          73: monitorexit
          74: aload         4
          76: athrow
          77: return
        Exception table:
           from    to  target type
               7    17    20   any
              20    23    20   any
              32    42    45   any
              45    48    45   any
              57    67    70   any
              70    74    70   any

    每一个synchronized块都对应一个monitorenter和两个monitorexit,其实JIT编译器在执行动态编译时会对上面代码进行优化:若发现前后相邻的synchronized块使用的是同一个锁对象,那么它就会把这几个synchronized块给合并为一个较大的同步块,这样做的好处在于线程在执行这些代码时,就无需频繁申请与释放锁了,从而达到申请与释放锁一次,就可以执行完全部的同步代码块,从而提升了性能。

  • 相关阅读:
    Java 处理cookie的方法
    HTML5的新标签-整体布局
    Git学习文档——文件状态git status
    Css中路径data用法
    python2
    hangfire
    Nginx系列~Nginx服务启动不了
    git形成本地仓库并从远处url拉取
    orcal和sql server中的字符串查找函数
    Eclipse 修改项目名称
  • 原文地址:https://www.cnblogs.com/webor2006/p/11448673.html
Copyright © 2011-2022 走看看