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

上一篇文章带大家简单了解了对象头,及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
  • 有一个java对象,上了一把synchronized(重量级锁),当有线程获取到锁时,其对象头中的Mark Word变成了指向Monitor的指针。(原本mark word当中的内容会存储到Monitor当中,释放时会取出这些内容再次放到mark word。)

  • thread3 来竞争这把锁,此时只有它自己,那么thread3将会被设置为Monitor的Owner,有且只能有一个Owner。

  • 如果thread3持有锁的过程中,如果thread4和thread5也来竞争锁,就会添加到EntryList当中,此时线程将被阻塞(BLOCKED)。

  • 当thread执行完同步代码块当中的内容,会唤醒EntryList当中的线程来竞争锁,此竞争是非公平的。

  • 另外,在WaitSet当中的thread1和thread2,其状态是WAITING,表示他们之前获得过锁,但是条件不满足,此处不讲解,后面在分析wait,notify时讲解。

二、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){
            
        }
    }
  • 当线程尝试获取这把锁的时候,会创建锁记录(Lock Record),每个线程的栈帧(线程执行到的方法,都会生成对应的栈帧),都会包含一个锁记录的结构,可以用来存储对象的Mark Word,如下所示:
轻量级锁 (2).png
  • 让锁记录中 Object Reference 指向锁对象,并尝试用 CAS 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录(如图上的1),并将锁记录的地址存入Object的Mark Word(如图上的2),如下所示:
轻量级锁 (1).png
  • 如果CAS成功,对象的Mark Word将会存储Lock Record 地址 和 锁状态 00,如下图所示:
轻量级锁 (3).png
  • 如果CAS失败,此时会有两种情况:
    • 如果是其他线程已经持有了该轻量级锁,则表示发生竞争,此时进入锁膨胀。
    • 如果是自己执行了 synchronized 锁重入(就像我们示例代码中的add()方法),那么再添加一条 Lock Record 作为重入的计数,只不过新添加的Lock Record中没有Object的Mark word内容,为null。如下所示:
轻量级锁 (4).png
  • 当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一,将null的Lock Record删除。

  • 当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 CAS 将 Mark Word 的值恢复给对象头。

    • 成功,则解锁成功。
    • 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容