ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

(4.6)轻量级锁(锁膨胀、锁自选、偏向锁、锁消除)

2022-08-24 16:34:27  阅读:172  来源: 互联网

标签:MarkWord 4.6 对象 加锁 线程 自选 轻量级 偏向


4.6 轻量级锁、偏向锁——Monitor升级

JDK6之前的加锁方式是:关联锁对象到Monitor进行加锁,Monitor是由操作系统提供的,加锁代价高。

JDK6之后,对加锁方式进行了优化,引入了轻量级锁、偏向锁等。

1. 轻量级锁

如果一个对象虽然有多个线程要加锁,但是加锁的时间是错开的(没有竞争),可以使用轻量级锁来优化。

如果这时有其他线程来获取轻量级锁,则被阻塞,加锁线程将轻量级锁升级为Monitor锁

假设有两个方法同步块,利用同一个对象加锁

private static final Object lock = new Object();

public static void method1() {
        synchronized (lock) {
            log.info("method1....");
            method2();
        }
}

private static void method2() {
        synchronized (lock) {
            log.info("method2...");
        }
}
  • synchronized加锁时,在栈帧中创建锁记录(Lock Record)。 锁记录的组成:

    • 锁对象指针(Lock Record address):锁对象指针记录锁对象的地址
    • 锁记录地址(Object reference):锁记录地址记录锁对象的Markword。
  • Object reference指向锁对象;然后尝试用锁记录地址CAS替换锁对象的MarkWord。

  • 如果成功,则MarkWord的值存入锁记录中,对象头的MarkWord存储了锁记录地址和状态00

  • 如果失败,则说明

    • 对象锁已经持有了其他线程的轻量级锁(对象头的状态为00),这时表示有竞争,进入锁膨胀状态
    • 对象锁持有了自己的锁(锁对象状态为00, 并且存储的锁记录地址是自己的锁记录的地址),表示执行了synchronized锁重入,则在栈帧中在创建一条锁地址为null的LockRecord,用于重入计数

  • 解锁时,是null的锁记录,表示有重入,这时清除锁记录,同时重入计数-1

  • 解锁时,是不为null的锁记录,则尝试cas恢复MarkWord给锁对象

    • 成功,解锁成功
    • 失败,则说明有竞争,轻量级锁已经升为重量级锁,进入重量级锁解锁流程
2. 锁膨胀

线程尝试CAS加轻量级锁时,失败,如果情况是有其他线程已经对锁对象加上了轻量级锁,则进行锁膨胀,将轻量级锁变为重量级锁。

  • 线程1尝试CAS加锁时,线程0已经对该对象加了轻量级锁

  • 进入锁膨胀。为锁对象申请Monitor锁,让锁对象的MarkWord指向Monitor,状态变为10; Monitor的Own指向线程0;自己进入EntryList进入阻塞状态

  • 线程0解锁时,尝试CAS将MarkWord恢复给锁对象,失败。说明已经锁膨胀。通过锁对象的MarkWord找到Monitor地址;将Owner设为null;唤醒EntryL中的阻塞线程,让阻塞线程获取重量级锁;Monitor记录线程的Hashcode。
  • 线程1解锁时,没有阻塞线程,将锁Hashcode age bias 01恢复给锁对象,解锁。
3. 锁自旋

重量级锁竞争时,竞争线程进入EntryList阻塞前,还可以使用自旋来优化,如果自旋成功,就避免了阻塞带来的上下文切换。

JDK6的自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会 高,就多自旋几次;反之,就少自旋甚至不自旋。

JDK7之后无法控制是否开启自旋锁。

优点:

  • 避免阻塞带来的上下文切换

缺点:

  • 自旋会占用CPU时间,多核自旋才能发挥优势
4. 偏向锁

当轻量级锁(没有竞争,就自己这个线程)+ 锁重入时,每次都需要生成锁记录,并尝试CAS替换对象头的MarkWord操作。

JDK6引入偏向锁做进一步优化: 只有第一次加锁时,不需要生成锁记录,将线程ID设置到锁对象的MarkWord中;之后锁重入时,检查MarkWord是否是自己的线程ID,只要不发生竞争,则可以一直使用偏向锁。

优点:避免了每次加锁都需要生成锁记录,并CAS的过程。

5. 撤销偏向锁

(1)调用hashcode

调用hashcode会撤销偏向锁,因为偏向锁的锁对象MarkWord存储的是线程ID,调用偏向锁会导致偏向锁被撤销。

  • 轻量级锁在锁记录中存储hashcode
  • 重量级锁在Monitor中记录hashcode

(2) 其他线程交错使用锁对象

会将偏向锁升级为轻量级锁

(3) 其他线程竞争使用锁对象

将偏向锁升级为重量级锁

(4) 调用wait/notify

wait、notify是重量级锁才有的机制,调用它们会将偏向锁升级为重量级锁

6. 批量重偏向

锁对象偏向线程1,这时有线程2交替获取锁,会撤销包含线程1ID的偏向锁(线程ID变为线程2ID,释放锁后,偏向锁ID恢复为线程1ID);

当阈值超过20此后,会在加锁时重新偏向线程2(即将锁对象的线程ID替换为线程2的ID)

7. 批量撤销

当撤销偏向锁次数超过40,JVM意识到重偏向也不对,于是整个类的所有对象都变为不可偏向锁,新创建的对象也是不可偏向锁。

8. 锁消除

如果锁对象是局部的、不同享的、没有竞争的,则JIT即时编译器会对代码进行优化,执行时不加锁。提高运行效率。

锁消除默认开启,关闭锁消除:

java -XX:-EliminateLocks -java HelloConcurrent.jar

手动开启锁消除

java -XX:EliminateLock -java HelloConcurrent.jar

标签:MarkWord,4.6,对象,加锁,线程,自选,轻量级,偏向
来源: https://www.cnblogs.com/greengages/p/16620550.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有