HotSpot虚拟机的锁优化
面试中多次被问到synchronized关键字的实现原理,一直认为仅是monitorenter与monitorexit两条指令而已,原来底层涉及到多种锁优化策略,包括:自旋锁,轻量锁,偏向锁。
1、自旋锁
互斥同步对性能影响最大的部分是线程的阻塞与恢复,因为这两个操作涉及用户态与内核态的转换。如果共享数据锁定时间很短,而且竞争不是特别激烈,那么阻塞实现并不划算。因此在多核的处理器中,我们可以让请求锁的线程进行忙等,而不是放弃处理器执行时间。反之,如果锁占用时间比较长,那忙等的线程只会白白浪费处理器资源,因此需要有一定的时间限制,超出限制便挂起线程。
使用AtomicReference模拟自旋锁:
public class SpinLock {
private AtomicReference<Thread> sign =new AtomicReference<>();
public void lock(){
Thread current = Thread.currentThread();
while(!sign .compareAndSet(null, current));
}
public void unlock (){
Thread current = Thread.currentThread();
sign .compareAndSet(current, null);
}
}
2、轻量锁
轻量锁是相对于阻塞实现的重量锁而言的,很多时候虽然用到锁,但并不存在竞争情况,相当于多个线程轮流获取一把锁。轻量锁可使用CAS操作获取,被多线程竞争时会膨胀为重量锁。
每个对象的对象头中都有一个字,称为mark word,低两位代表状态,根据状态的不同,其余各位有不同的含义:

HotSpot虚拟机以一种叫做displaced headers的方法实现轻量锁。当线程获取轻量锁时,会在该线程的栈上创建一个lock record(lock record由一个mark word备份,一个指向对象的指针组成),然后备份当前mark word,最后使用CAS操作把mark word的状态位更新为Thin locked,也就是00,同时mark word中的指针指向lock record。

发生重入时,线程可以知道对象mark word中的指针正指向自己的栈,便创建一个值为NULL的lock record,在释放锁时如果lock record值为NULL就立即返回。
当轻量锁被一个线程获得后,另一个希望获取锁的线程会将轻量锁膨胀为重量锁,该线程在一个循环里不断尝试,直到膨胀成功。膨胀也会发生在锁的wait或notify方法被调用时,无论该锁是否已被一个线程获取。
进行膨胀时,首先用CAS将mark word中的状态位更新为Inflating,尝试获取处于该状态锁的线程会等待到膨胀完成,已经获取到锁的线程也必须等到膨胀完成才能释放锁。
3、偏向锁
前文说到轻量锁适合的场景是多个线程轮流占用资源,而偏向锁适用的场景是一个线程反复占用资源。什么时候会出现这种情况呢?比如在使用一些类库时,它的接口是线程安全的,但我们不会通过多个线程访问。
在Unlocked状态下,mark word的第三位代表是否允许启用偏序。如果那一位为0,说明锁没有被任何线程获取,且不允许偏序。如果为1,锁可以是如下状态中的一种:
- 匿名偏向:线程指针为NULL,锁还没有偏向于任何一个线程,这是允许偏向的锁的初始状态。
- 可重偏向:mark word中的epoch字段无效,获取锁的线程可使锁偏向自己。
- 已偏向:线程指针不为NULL,epoch字段有效,锁正偏向于线程指针指向的线程。
由于偏向锁需要占用hashcode字段存放线程指针,访问该对象的hashcode将会导致偏向状态被撤销(Object类的hashcode的方法会导致这种情况)。
偏向锁使用的lock record与轻量锁一致,但是mark word备份没有被使用,在偏向被撤销时,会被转化为轻量锁。

偏序撤销在系统到达全局安全点时执行,竞争线程通过遍历偏序线程的lock record判断锁是否正在被占用, 将其转化为轻量锁或偏向自己。

参考:《Evaluating and improving biased locking in the HotSpot virtual machine》
HotSpot虚拟机的锁优化的更多相关文章
- 深入理解多线程(五)—— Java虚拟机的锁优化技术
本文是<深入理解多线程>的第五篇文章,前面几篇文章中我们从synchronized的实现原理开始,一直介绍到了Monitor的实现原理. 前情提要 通过前面几篇文章,我们已经知道: 1.同 ...
- Java 虚拟机对锁优化所做的努力
作为一款公用平台,JDK 本身也为并发程序的性能绞尽脑汁,在 JDK 内部也想尽一切办法提供并发时的系统吞吐量.这里,我将向大家简单介绍几种 JDK 内部的 "锁" 优化策略. 1 ...
- Java虚拟机对锁优化所做的努力(读书笔记)
锁偏向 是一种加锁操作的优化手段,他的核心思想是:如果一个线程获得了锁,那么就进入偏向模式,当这个线程再次请求锁时,无须在做任何同步操作,因此在几乎没有锁竞争的场合,偏向锁是比较好的优化效果 ...
- Java虚拟机的锁优化
1 锁偏向.当现成请求一个对象锁时,如果获得锁,则该对象锁进入偏向模式,当该线程再次请求该对象的锁时,无需再做任何同步操作. 可通过在Java虚拟机中开启参数-XX:+UseBasedLock开启偏向 ...
- 《深入理解Java虚拟机》-----第13章 线程安全与锁优化
概述 在软件业发展的初期,程序编写都是以算法为核心的,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间中的客体,程序代码则用于处理这些数据,这种思维方式直接站在计算机的角度去抽象问题和解 ...
- 《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化
<深入了解java虚拟机>高效并发读书笔记--Java内存模型,线程,线程安全 与锁优化 本文主要参考<深入了解java虚拟机>高效并发章节 关于锁升级,偏向锁,轻量级锁参考& ...
- Java虚拟机13:互斥同步、锁优化及synchronized和volatile
互斥同步 互斥同步(Mutual Exclusion & Synchronization)是常见的一种并发正确性保证手段.同步是指子啊多个线程并发访问共享数据时,保证共享数据在同一时刻只能被一 ...
- 深入理解java虚拟机(7)---线程安全 & 锁优化
关于线程安全的话题,足可以使用一本书来讲解这些东西.<Java Concurrency in Practice> 就是讲解这些的,在这里 主要还是分析JVM中关于线程安全这块的内容. 1. ...
- 深入理解Java虚拟机读书笔记9----线程完全与锁优化
九 线程完全与锁优化 1 Java语言中的线程完全 ---线程安全:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用 ...
随机推荐
- 锐捷交换机如何配置远程管理地址(telnet)
基本命令如下: hostname(config)#username admin password 123456 ------>telnet 登录账号为admin密码为123456 hostna ...
- .NET ftp文件上传和下载
文章参考来源地址:https://blog.csdn.net/wybshyy/article/details/52095542 本次对代码进行了一点扩展:将文件上传到ftp指定目录下,若目录不存在则创 ...
- 【python库】tqdm介绍及常用方法
前言 Tqdm 是一个快速,可扩展的Python进度条,可以在 Python 长循环中添加一个进度提示信息,用户只需要封装任意的迭代器 tqdm(iterator).具体使用可以查看官网. 操作 fr ...
- Zabbix主动模式与被动模式的区别——最简单的解释
一直搞不清楚Zabbix的主动模式和被动模式的差别,网上看到别人博客里的解释都是云里雾里的,完全搞不清.知道偶然看到了以下这个解释.就基本上明白了. Zabbix的主动模式和被动模式都是相对agent ...
- [UE4] TSharedPtr, TWeakObjectPtr and TUniquePtr
转自:https://dawnarc.com/2018/07/ue4-tsharedptr-tweakobjectptr-and-tuniqueptr/ UE4 的 TSharedPtr.TWeakO ...
- 【Linux】修改root密码
sudo passwd root 然后提示输入两次新密码就可以了
- GridControl单元格编辑验证的方法
本文实例演示了DevExpress实现GridControl单元格编辑验证的方法,比较实用的功能,具体方法如下: 主要功能代码如下: /// <summary> /// 自定义单元格验证 ...
- git 学习笔记 --Bug分支
软件开发中,bug就像家常便饭一样.有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除. 当你接到一个修复一 ...
- 【2】【典型一维动态规划】【剑指offer+leetcode53】连续子数组的最大和
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果向量中包含负数 ...
- ELK学习笔记之logstash将配置写在多个文件
0x00 概述 我们用Logsatsh写配置文件的时候,如果读取的文件太多,匹配的正则过多,会使配置文件动辄成百上千行代码,可能会造成阅读和修改困难.这时候,我们可以将配置文件的输入.过滤.输出分别放 ...