面试中多次被问到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虚拟机的锁优化的更多相关文章

  1. 深入理解多线程(五)—— Java虚拟机的锁优化技术

    本文是<深入理解多线程>的第五篇文章,前面几篇文章中我们从synchronized的实现原理开始,一直介绍到了Monitor的实现原理. 前情提要 通过前面几篇文章,我们已经知道: 1.同 ...

  2. Java 虚拟机对锁优化所做的努力

    作为一款公用平台,JDK 本身也为并发程序的性能绞尽脑汁,在 JDK 内部也想尽一切办法提供并发时的系统吞吐量.这里,我将向大家简单介绍几种 JDK 内部的 "锁" 优化策略. 1 ...

  3. Java虚拟机对锁优化所做的努力(读书笔记)

    锁偏向      是一种加锁操作的优化手段,他的核心思想是:如果一个线程获得了锁,那么就进入偏向模式,当这个线程再次请求锁时,无须在做任何同步操作,因此在几乎没有锁竞争的场合,偏向锁是比较好的优化效果 ...

  4. Java虚拟机的锁优化

    1 锁偏向.当现成请求一个对象锁时,如果获得锁,则该对象锁进入偏向模式,当该线程再次请求该对象的锁时,无需再做任何同步操作. 可通过在Java虚拟机中开启参数-XX:+UseBasedLock开启偏向 ...

  5. 《深入理解Java虚拟机》-----第13章 线程安全与锁优化

    概述 在软件业发展的初期,程序编写都是以算法为核心的,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间中的客体,程序代码则用于处理这些数据,这种思维方式直接站在计算机的角度去抽象问题和解 ...

  6. 《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化

    <深入了解java虚拟机>高效并发读书笔记--Java内存模型,线程,线程安全 与锁优化 本文主要参考<深入了解java虚拟机>高效并发章节 关于锁升级,偏向锁,轻量级锁参考& ...

  7. Java虚拟机13:互斥同步、锁优化及synchronized和volatile

    互斥同步 互斥同步(Mutual Exclusion & Synchronization)是常见的一种并发正确性保证手段.同步是指子啊多个线程并发访问共享数据时,保证共享数据在同一时刻只能被一 ...

  8. 深入理解java虚拟机(7)---线程安全 & 锁优化

    关于线程安全的话题,足可以使用一本书来讲解这些东西.<Java Concurrency in Practice> 就是讲解这些的,在这里 主要还是分析JVM中关于线程安全这块的内容. 1. ...

  9. 深入理解Java虚拟机读书笔记9----线程完全与锁优化

    九 线程完全与锁优化   1 Java语言中的线程完全         ---线程安全:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用 ...

随机推荐

  1. WPF Button LinkButton 绑定多个值 Template

    效果如下: 代码如下: <Button Click="InventoryDetail_OnClick" Template="{StaticResource Inve ...

  2. postgrelsql base64加密,JS base64解密

    项目中做云桌面对接,需要在项目中查出用户的明文密码,拼接到云桌面登陆地址中,防止明文传输,做了base64加密解密,防止小白黑客盗取用户密码. postgrelsql base64加密SQL语句: 查 ...

  3. oracle 解决 exp 空表不能导出的问题

    原因:oralce_11g 中有个新特性,当表无数据时,不分配 segment,以节省空间,这也就导致了 exp 在导出表时,没有数据的表会被忽略 方法一:我们可以向表中插入数据,在删除,这样数据表就 ...

  4. 【bat批处理】批量执行某个文件夹下的所有sql文件bat批处理

    遍历文件夹下所有的sql文件,然后命令行执行 for /r "D:\yonyou\UBFV60\U9.VOB.Product.Other" %%a in (*.sql) do ( ...

  5. keystone源码阅读--python函数

    按照setup.sfg文件中[entry_poubts]中的声明前后阅读: 1.cmd.manage:main os.path.join(path,name):连接目录与文件名或目录os.path.e ...

  6. jquery监听回车

    jquery监听回车 <pre> $("#loginusername, #loginpassword, #code").keydown(function() { if( ...

  7. ChecklistForTest

    相关字段内容较长时,页面显示是否正确(包括各主页面.明细页面.打印预览页面) 数据量较多时,页面显示是否正确(包括各主页面.明细页面.打印预览页面) 各字段为空校验(都为空,部分为空,都不为空)是否正 ...

  8. mysql 启动 && 停止

    启动:service mysql start关闭:service mysql stop查进程:ps aux | grep mysql杀进程:kill -9 mysqlID Good Good Stud ...

  9. Django框架深入了解_05 (Django中的缓存、Django解决跨域流程(非简单请求,简单请求)、自动生成接口文档)

    一.Django中的缓存: 前戏: 在动态网站中,用户所有的请求,服务器都会去数据库中进行相应的增,删,查,改,渲染模板,执行业务逻辑,最后生成用户看到的页面. 当一个网站的用户访问量很大的时候,每一 ...

  10. day53——标签操作

    day53 今日内容 标签操作 值操作 取值: 文本输入框:$('#username').val(); input,type=radio单选框: $('[type="radio"] ...