synchronized 的实现原理

Synchronized 在 JVM 里的实现都是基于进入和退出 Monitor 对象来实现方法 同步和代码块同步,虽然具体实现细节不一样,但是都可以通过成对的 MonitorEnter 和 MonitorExit 指令来实现。

1.对同步块,MonitorEnter 指令插入在同步代码块的开始位置,当代码执行到 该指令时,将会尝试获取该对象 Monitor 的所有权,即尝试获得该对象的锁,而 monitorExit 指令则插入在方法结束处和异常处,JVM 保证每个 MonitorEnter 必须 有对应的 MonitorExit。
2.对同步方法,从同步方法反编译的结果来看,方法的同步并没有通过指令 monitorenter 和 monitorexit 来实现,相对于普通方法,其常量池中多了 ACC_SYNCHRONIZED 标示符。
JVM 就是根据该标示符来实现方法的同步的:当方法被调用时,调用指令将 会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线 程将先获取 monitor,获取成功之后才能执行方法体,方法执行完后再释放 monitor。在方法执行期间,其他任何线程都无法再获得同一个 monitor 对象。

synchronized 使用的锁是存放在 Java 对象头里面,具体位置是对象头里面的 MarkWord,MarkWord 里默认数据是存储对象的 HashCode 等信息,但是会随着对象的运行改变而发生变化,不同的锁状态对应着不同的记录存 储方式

锁的状态

一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态, 它会随着竞争情况逐渐升级。锁可以升级但不能降级,目的是为了提高获得锁和 释放锁的效率。

自旋锁

1.原理

自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么 那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状 态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这 样就避免用户线程和内核的切换的消耗。
但是线程自旋是需要消耗 CPU 的,说白了就是让 CPU 在做无用功,线程不 能一直占用 CPU 自旋做无用功,所以需要设定一个自旋等待的最大时间。
如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,就会 导致其它争用锁的线程在最大等待时间内还是获取不到锁,这时争用线程会停止 自旋进入阻塞状态。

2.自旋锁的优缺点

自旋锁尽可能的减少线程的阻塞,这对于锁的竞争不激烈,且占用锁时间非 常短的代码块来说性能能大幅度的提升,因为自旋的消耗会小于线程阻塞挂起操 作的消耗!
但是如果锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块, 这时候就不适合使用自旋锁了,因为自旋锁在获取锁前一直都是占用 cpu 做无用 功,线程自旋的消耗大于线程阻塞挂起操作的消耗,其它需要cup 的线程又不能获取到 cpu,造成 cpu 的浪费。

3.自旋锁时间阈值

自旋锁的目的是为了占着 CPU 的资源不释放,等到获取到锁立即进行处理。 但是如何去选择自旋的执行时间呢?如果自旋执行时间太长,会有大量的线程处 于自旋状态占用 CPU 资源,进而会影响整体系统的性能。因此自旋次数很重要
JVM 对于自旋次数的选择,jdk1.5 默认为 10 次,在 1.6 引入了适应性自旋锁, 适应性自旋锁意味着自旋的时间不在是固定的了,而是由前一次在同一个锁上的 自旋时间以及锁的拥有者的状态来决定,基本认为一个线程上下文切换的时间是 最佳的一个时间。
JDK1.6 中-XX:+UseSpinning 开启自旋锁; JDK1.7 后,去掉此参数,由 jvm 控 制;

偏向锁

1.引入背景:

大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多 次获得,为了让线程获得锁的代价更低而引入了偏向锁,减少不必要的 CAS 操作。

2.偏向锁定义

顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中, 同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步 的,减少加锁/解锁的一些 CAS 操作(比如等待队列的一些 CAS 操作),这种 情况下,就会给线程加一个偏向锁。 如果在运行过程中,遇到了其他线程抢占 锁,则持有偏向锁的线程会被挂起,JVM 会消除它身上的偏向锁,将锁恢复到标 准的轻量级锁。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的 运行性能。

3.偏向锁获取过程:

步骤 1、 访问 Mark Word 中偏向锁的标识是否设置成 1,锁标志位是否为01,确认为可偏向状态。
步骤 2、 如果为可偏向状态,则测试线程 ID 是否指向当前线程,如果是,进入步骤 5,否则进入步骤 3。
步骤 3、 如果线程 ID 并未指向当前线程,则通过 CAS 操作竞争锁。如果竞 争成功,则将 Mark Word 中线程 ID 设置为当前线程 ID,然后执行 5;如果竞争 失败,执行 4。
步骤 4、 如果 CAS 获取偏向锁失败,则表示有竞争。当到达全局安全点 (safepoint)时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞 在安全点的线程继续往下执行同步代码。(撤销偏向锁的时候会导致 stop the word)
步骤 5、 执行同步代码。

4.偏向锁的释放:

偏向锁的撤销在上述第四步骤中有提到。偏向锁只有遇到其他线程尝试竞争偏向 锁时,持有偏向锁的线程才会释放偏向锁,线程不会主动去释放偏向锁。偏向锁 的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首 先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复 到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

5.偏向锁的适用场景

始终只有一个线程在执行同步块,在它没有执行完释放锁之前,没有其它线程去 执行同步块,在锁无竞争的情况下使用,一旦有了竞争就升级为轻量级锁,升级 为轻量级锁的时候需要撤销偏向锁,撤销偏向锁的时候会导致 stop the word 操 作;在有锁的竞争时,偏向锁会多做很多额外操作,尤其是撤销偏向所的时候会导致 进入安全点,安全点会导致 stw,导致性能下降,这种情况下应当禁用。
jvm 开启/关闭偏向锁
开启偏向锁:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 关闭偏向锁:-XX:-UseBiasedLocking

轻量级锁

轻量级锁是由偏向锁升级来的,偏向锁运行在一个线程进入同步块的情况下,当 第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁;

轻量级锁的加锁过程:

在代码进入同步块的时候,如果同步对象锁状态为无锁状态且不允许进行偏向 (锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程 的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的 Mark Word 的拷贝,官方称之为 Displaced Mark Word。
拷贝成功后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock Record 的指针,并将 Lock record 里的 owner 指针指向 object mark word。如果更 新成功,则执行步骤 4,否则执行步骤 5。
如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象 Mark Word 的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态
如果这个更新操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前 线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进 入同步块继续执行。否则说明多个线程竞争锁,当竞争线程尝试占用轻量级锁失 败多次之后,轻量级锁就会膨胀为重量级锁,重量级线程指针指向竞争线程,竞 争线程也会阻塞,等待轻量级线程释放锁后唤醒他。锁标志的状态值变为“10”, Mark Word 中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也 要进入阻塞状态。

Android java synchronized原理相关推荐

  1. java synchronized 原理_Java Synchronized的原理

    我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的: public classSynchronizedDemo{public voidmethod(){synchro ...

  2. java eventbus 原理_本文为 Android 开源项目实现原理解析 EventBus 部分,从源码分析 EventBus 的实现原理...

    之前太忙导致 Android 开源项目实现原理解析 一度搁浅,目前一期进行中,我也完成了 EventBus 分析的初稿,大家可以稍微看看后面会继续润色下. PS:本文直接复制 Markdown,格式有 ...

  3. Java Synchronized 重量级锁原理深入剖析上(互斥篇)

    前言 线程并发系列文章: Java 线程基础 Java 线程状态 Java "优雅"地中断线程-实践篇 Java "优雅"地中断线程-原理篇 真正理解Java ...

  4. android r.java 原理,深入理解Android消息处理系统原理

    Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制.实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制. Android通过Looper.Ha ...

  5. Java 并发编程CAS、volatile、synchronized原理详解

    CAS(CompareAndSwap) 什么是CAS? 在Java中调用的是Unsafe的如下方法来CAS修改对象int属性的值(借助C来调用CPU底层指令实现的): /*** * @param o ...

  6. java虚拟机线程调优与底层原理分析_啃碎并发(七):深入分析Synchronized原理...

    原标题:啃碎并发(七):深入分析Synchronized原理 前言 记得开始学习Java的时候,一遇到多线程情况就使用synchronized,相对于当时的我们来说synchronized是这么的神奇 ...

  7. Android 插件化原理学习 —— Hook 机制之动态代理

    前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...

  8. java synchronized关键字_Java:手把手教你全面学习神秘的Synchronized关键字

    前言 在Java中,有一个常被忽略 但 非常重要的关键字Synchronized 今天,我将详细讲解 Java关键字Synchronized的所有知识,希望你们会喜欢 目录 示意图 1. 定义 Jav ...

  9. Android插件化原理—ClassLoader加载机制

    前面<Android 插件化原理学习 -- Hook 机制之动态代理>一文中我们探索了一下动态代理 hook 实现了 启动没有在 AndroidManifest.xml 中显式声明的 Ac ...

最新文章

  1. C++实现桶排序——十大经典排序算法之九【GIF动画+完整代码+详细注释】
  2. 201521123078 《Java程序设计》第6周学习总结
  3. 时间序列研(part1)--随机过程
  4. libsvm java下载_一个基于LIBSVM(JAVA)的股票预测demo
  5. python中字母用什么表示_python中字母与ascii码的相互转换
  6. java dojo_Dojo入门三种HelloWorld!
  7. 从fragment启动另一个fragment
  8. TypeSrcript如何引入第三方库 如果加d.ts以及async await如何使用 demo,只有代码,文字后续补充...
  9. ActiveRecord 复合主键时 实体转换为DataRow
  10. CentOS 5.3 安装后的基本软件配置
  11. 机器视觉运动控制一体机应用例程 | 瓶盖密封完整性检测
  12. 万兆交换机用什么网线_万兆交换机SFP+端口是否能与千兆交换机SFP端口连接?...
  13. 【10.28 校内普及组】 小B浇花 题解
  14. 使用 __breakpoint 实现软件中断
  15. FITC-GSL I荧光素标记的西非单叶豆凝集素 I
  16. 什么是Hypervisor
  17. mac启动terminal终端快捷键
  18. 计算机辅助英语训练新方法,一种新的计算机辅助英语教学模式
  19. GitHub 上的前端项目收集
  20. 太难了!微信借钱语音确认过还被骗了

热门文章

  1. 苍穹战线PC版教程,TC Games教你如何畅玩苍穹战线二次元卡牌游戏
  2. TCPUDP测试工具 1.02 绿色版 - 绿色软件联盟 - 可定时发送数据
  3. [翻译]Gallery Server Pro ----用于分享相片,视频,音频及其他媒体的ASP.NET相册[Carol]...
  4. 通过Android系统日志监听程序启动事件
  5. web自动化如何在不同浏览器运行_从理论到工具:带你全面了解自动化测试框架...
  6. 【黑金ZYNQ7000系列原创视频教程】02.视频接口——hdmi编码输出实验
  7. 最好听的英文歌曲《Take Me To Your Heart》,清澈见底
  8. 爬虫——豆瓣电影top250
  9. 【微淘百课】微信多群直播服务,微课多群同步转播机器人,微信语音多群同步助手
  10. 三维扫描在建筑行业应用综述