转自:https://forum.idevfun.io/t/topic/235/2

Java 中,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。因此,这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”。JDK中对Synchronized做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”。

偏向锁

所谓的偏向锁是指在对象实例的Mark Word(说白了就是对象内存中的开头几个字节保留的信息,如果把一个对象序列化后明显可以看见开头的这些信息),为了在线程竞争不激烈的情况下,减少加锁及解锁的性能损耗(轻量级锁涉及多次CAS操作)在Mark Word中有保存这上次使用这个对象锁的线程ID信息,如果这个线程再次请求这个对象锁,那么只需要读取该对象上的Mark Word的偏向锁信息(也就是线程id)跟线程本身的id进行对比,如果是同一个id就直接认为该id获得锁成功,而不需要在进行真正的加解锁操作。其实说白了就是你上次来过了,这次又来,并且在这两次之间没有其他人来过,对于这个线程来说,锁对象的资源随便用都是安全的。这次用缓存来换取性能的做法,不过偏向锁在锁竞争不激烈的情景下使用才能获取比较高的效率。当使用CAS竞争偏向锁失败,表示竞争比较激烈,偏向锁升级为轻量级锁。

轻量级锁

所谓轻量级锁是比偏向锁更耗资源的锁,实现机制是,线程在竞争轻量级锁前,在线程的栈内存中分配一段空间作为锁记录空间(就是轻量级锁对应的对象的对象头的字段的拷贝),拷贝好后,线程通过CAS去竞争这个对象锁,试图把对象的对象头子段改成指向所记录空间,如果成功则说明获取轻量级锁成功,如果失败,则进入自旋(说白了就是循环)取试着获取锁。如果自旋到一定次数还是不能获取到锁,则进入重量级锁。

自旋锁

说白了就是获取锁失败后,为了避免直接让线程进入阻塞状态而采取的循环一定次数去试着获取锁的行为。(线程进入阻塞状态和退出阻塞状态是涉及到操作系统管理层面的,需要从用户态进入内核态,非常消耗系统资源),为什么能这样做呢,是因为试验证明,锁的持有时间一般是非常短的,所以一般多次尝试就能竞争到锁。

重量级锁

所谓的重量级锁,其实就是最原始和最开始java实现的阻塞锁。在JVM中又叫对象监视器。这时的锁对象的对象头字段指向的是一个互斥量,所有线程竞争重量级锁,竞争失败的线程进入阻塞状态(操作系统层面),并且在锁对象的一个等待池中等待被唤醒,被唤醒后的线程再次去竞争锁资源。

总结

所谓的锁升级,其实就是从偏向锁到轻量级锁(自旋锁)到重量级锁,之前一直被这几个概念困扰,网上的 文章解释的又不通俗易懂,其实说白了,一切一切的开始源于java对synchronized同步机制的性能优化,最原始的synchronized同步机制是直接跳过前几个步骤,直接进入重量级锁的,而重量级锁因为需要线程进入阻塞状态(从用户态进入内核态)这种操作系统层面的操作非常消耗资源,这样的话,synchronized同步机制就显得很笨重,效率不高。那么为了解决这个问题,java才引入了偏向锁,轻量级锁,自旋锁这几个概念。拿这几个锁有何优化呢?网上也没有通俗易懂的解释,其实说白了就是,偏向锁是为了避免CAS操作,尽量在对比对象头就把加锁问题解决掉,只有冲突的情况下才指向一次CAS操作,而轻量级锁和自旋锁呢,其实两个是一体使用的,为的是尽量避免线程进入内核的阻塞状态,这对性能非常不利,试图用CAS操作和循环把加锁问题解决掉,而重量级锁是最终的无奈解决方案,说白了就是能通过内存读取判断解决加速问题优于〉通过CAS操作和空循环优于〉CPU阻塞,唤醒线程。

(转)Java锁性能提高有哪些机制?的更多相关文章

  1. 转 : 深入解析Java锁机制

    深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...

  2. Java 锁机制总结

    锁的种类 独享锁 VS 共享锁 独享锁:锁只能被一个线程持有(synchronized) 共享锁:锁可以被多个程序所持有(读写锁) 乐观锁 VS 悲观锁 乐观锁:每次去拿数据的时候都乐观地认为别人不会 ...

  3. Java 性能优化手册 — 提高 Java 代码性能的各种技巧

    转载: Java 性能优化手册 - 提高 Java 代码性能的各种技巧 Java 6,7,8 中的 String.intern - 字符串池 这篇文章将要讨论 Java 6 中是如何实现 String ...

  4. 有助于提高"锁"性能的几点建议

    有助于提高"锁"性能的几点建议 1.减少锁持有时间 public synchronized void syncMethod() { othercode1(); mutextMeth ...

  5. java锁机制的面试题

    java锁机制的面试题 1.ABA问题 2.CAS乐观锁 3.synchronize实现原理 4.synchronize与lock的区别 5.volatile实现原理 6.乐观锁的业务场景及实现方式 ...

  6. Java锁机制深入理解

    Java锁机制 背景知识 指令流水线 ​ CPU的基本工作是执行存储的指令序列,即程序.程序的执行过程实际上是不断地取出指令.分析指令.执行指令的过程. ​ 几乎所有的冯•诺伊曼型计算机的CPU,其工 ...

  7. Java的性能优化

    http://www.toutiao.com/i6368345864624144897/?tt_from=mobile_qq&utm_campaign=client_share&app ...

  8. Java 应用性能调优实践

    Java 应用性能优化是一个老生常谈的话题,笔者根据个人经验,将 Java 性能优化分为 4 个层级:应用层.数据库层.框架层.JVM 层.通过介绍 Java 性能诊断工具和思路,给出搜狗商业平台的性 ...

  9. 将Web应用性能提高十倍的10条建议

    导读 提高 web 应用的性能从来没有比现在更重要过.网络经济的比重一直在增长:全球经济超过 5% 的价值是在因特网上产生的(数据参见下面的资料).这个时刻在线的超连接世界意味着用户对其的期望值也处于 ...

随机推荐

  1. MYSQL:基础——索引原理及慢查询优化

    MYSQL:基础——索引原理及慢查询优化 索引的数据结构 索引的数据结构是B+树.如下图所示,B+树的节点通常被表示为一组有序的数据项和子指针.图中第一个节点包含数据项3和5,包含三个指针,第一个指针 ...

  2. 【GStreamer开发】GStreamer基础教程09——收集媒体信息

    目标 有时你需要快速的了解一个文件(或URI)包含的媒体格式或者看看是否支持这种格式.当然你可以创建一个pipeline,设置运行,观察总线上的消息,但GStreamer提供了一个工具可以帮你做这些. ...

  3. MySQL的join on和 where 的执行顺序和区别,以及各种连接说明

    目录 1.各种连接的解读说明: 1.1.各种连接的含义和说明 1.1.1 所有连接分类 1.1.2 left join 和 left outer join 区别 1.2.神图参考 1.4.一些参考说明 ...

  4. shell学习及脚步编写

    目录: shell基础变量逻辑运算符 scp基础用法脚本 while+for+case基础用法脚本 内核优化脚本 自动修改本机ip脚本 for+case 查询日志脚本 一键yum安装lamp脚本 源码 ...

  5. dockerui 安装

    meiya@meiya:~$ docker pull abh1nav/dockerui Using default tag: latest latest: Pulling from abh1nav/d ...

  6. js判断json对象是否为空

    if("{}" == JSON.stringify(json对象)) { // 满足条件就是空 }

  7. LeetCode 896. 单调数列(Monotonic Array)

    896. 单调数列 896. Monotonic Array 题目描述 如果数组是单调递增或单调递减的,那么它是单调的. 如果对于所有 i<=j,A[i]<=A[j],那么数组 A 是单调 ...

  8. 015 Android md5密码加密及其工具类

    1.md5加密介绍 MD5算法是广泛使用的杂凑函数,也就是哈希函数,英文全拼是:Message Digest Algorithm,对应的中文名字是消息摘要算法. MD5加密:将字符串转换成 32位的字 ...

  9. Prometheus入门到放弃(2)之Node_export安装部署

    1.下载安装 node_exporter服务需要在三台机器都安装,这里我们以一台机器为例: 地址:https://prometheus.io/download/ ### 另外两个节点部署时,需要先创建 ...

  10. 剑指offer11:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。(进制转换,补码反码)

    1. 题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 2. 思路和方法 使用移位(<<)和 “| & !”操作来实现.1的二进制是:前面都是0,最后一位 ...