面试精选一些收藏Java 进阶

java并发编程(六)synchronized原理 及 轻量级锁

2021-11-26  本文已影响0人  我犟不过你

上一篇文章带大家简单了解了对象头,及mark word的内容,那么本文将来学习,mark word到底有什么作用。其实就是synchronized的原理。

先将64位虚拟机的java对象Mark Word放在这,方便后面查看:

|----------------------------------------------------------------------------------------------|
|                                   Mark Word(64bits)                     |      State         |
|----------------------------------------------------------------------------------------------|
|    unused:25|identity_hashcode:31|unused:1|age:4|biase_lock:0| lock:01  |      Nomal         |
|----------------------------------------------------------------------------------------------|
|    thread:54|      epoch:2       |unused:1|age:4|biase_lock:1| lock:01  |      Biased        |
|----------------------------------------------------------------------------------------------|
|                        ptr_to_lock_record:62                 | lock:00  | Lightweight Locked |
|----------------------------------------------------------------------------------------------|
|                       ptr_to_heavyweight_monitor:62          | lock:10  | Heavyweight Locked |
|----------------------------------------------------------------------------------------------|
|                                                              | lock:11  |    Marked for GC   |
|----------------------------------------------------------------------------------------------|

一、Monitor

在介绍Mark Word的时候,提到过Monitor,可以称为监视器或管程,下面我们看下它到底是个什么东西。

每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针。不加 synchronized 的对象不会关联Monitor。

首先通过下图展示管程的组成,以及和java对象的关系:

Monitor管程 (2).png

二、synchronized原理分析

2.1 synchronized字节码分析

在上面了解Monitor后,进入java当中的重点,synchronized的学习。

有如下代码:

    static final Object object = new Object();
    static int i = 0;

    public static void main(String[] args) {
        synchronized (object) {
            i++;
        }
    }

其字节码如下所示:

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // 获取锁对象object
       3: dup                               // 拷贝一份
       4: astore_1                          // 将拷贝的变量存储到 slot 1中
       5: monitorenter                      // 将Monitor指针设置到lock对象的Mark Word
       6: getstatic     #3                  // 获取静态变量i
       9: iconst_1                          // 准备常数 1
      10: iadd                              // 执行++ 操作
      11: putstatic     #3                  // 将i当前值赋值给静态变量
      14: aload_1                           // 获取对象锁
      15: monitorexit                       // 重置对象的Mark Word,唤醒EntryList当中的线程
      16: goto          24                  // 跳转到24 行
      19: astore_2                          // 将异常存储到 slot 2 中
      20: aload_1                           // 获取锁对象
      21: monitorexit                       // 重置对象的Mark Word,唤醒EntryList当中的线程
      22: aload_2                           // 获取slot 2中的异常
      23: athrow                            // 抛出异常
      24: return                            // 方法返回
    Exception table:
       from    to  target type
           6    16    19   any              // 6 ~ 16 行为正常代码运行逻辑,如果在这之间发生了异常,则代码跳转值第19行
          19    22    19   any              // 19 ~ 22 是异常时,代码执行的逻辑

关于上面字节码的意思都在注释中。

注意:在方法层面上的锁,在字节码当中不会有锁体现,如下:

    static final Object object = new Object();
    static int i = 0;

    public static synchronized void add() {
        i++;
    }

    public static void main(String[] args) {
        add();
    }

字节码如下:

public static synchronized void add();
    Code:
       0: getstatic     #2                  // Field i:I
       3: iconst_1
       4: iadd
       5: putstatic     #2                  // Field i:I
       8: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #3                  // Method add:()V
       3: return

2.2 轻量级锁

如开篇展示的对象Mark Word,共有5种锁的状态, 我们本小节讲解其中之一,轻量级锁相关的内容。

轻量级锁是指在满足一定的条件内,使用CAS(自旋)来尝试获取对象锁的一种机制,如果超过以下条件,则会膨胀为重量级锁:

1)在jdk1.6前,默认10次,可通过-XX:PreBlockSpin来修改,或者自旋线程数超过CPU核数的一半。

2)jdk1.6之后,引入了自适应自旋锁,次数并非一成不变。根据获取锁的成功率来决定是否能有更长的等待时间。

轻量级锁仍然是使用synchronized,用户其实是无感知的。

2.2.1 轻量级锁的上锁和释放锁

假设当前有一把对象锁lock,两个方法使用同一把锁,且add当中会调用sub()方法,如下所示:

    static final Object lock = new Object();

    public void add(){
        synchronized (lock){
            sub();
        }
    }

    public void sub(){
        synchronized (lock){
            
        }
    }
轻量级锁 (2).png 轻量级锁 (1).png 轻量级锁 (3).png 轻量级锁 (4).png
上一篇 下一篇

猜你喜欢

热点阅读