| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> JUC之synchronized原理阶段三 -> 正文阅读 |
|
[Java知识库]JUC之synchronized原理阶段三 |
友情提示学习更加详细内容查看:多线程锁synchronized挑战_亚索@哈塞给-CSDN博客 4.5 Monitor 概念Java 对象头以 32 位虚拟机为例,普通对象的对象头结构如下,其中的klass Word为指针,指向对应的Class对象; 数组对象 其中 Mark Word 结构为? ?所以一个对象的结构如下: Monitor 原理Monitor被翻译为监视器或者说管程 每个java对象都可以关联一个Monitor,如果使用 ?
?synchronized原理
反编译后的部分字节码
注意:方法级别的 synchronized 不会在字节码指令中有所体现 synchronized 原理进阶轻量级锁轻量级锁的使用场景是:如果一个对象虽然有多个线程要对它进行加锁,但是加锁的时间是错开的(也就是没有人可以竞争的),那么可以使用轻量级锁来进行优化。轻量级锁对使用者是透明的,即语法仍然是 友情提示:轻量级锁,没有线程的竞争,看着就像是串行一样
?1、每次指向到synchronized代码块时,都会创建锁记录(Lock Record)对象,每个线程都会包括一个锁记录的结构,锁记录内部可以储存对象的Mark Word和对象引用reference 友情提示:每一个线程都会包含锁记录的对象,内部包含对象的引用和储存对象的Mark Word. 2、 让锁记录中的Object reference指向对象,并且尝试用cas(compare and sweep)替换Object对象的Mark Word ,将Mark Word 的值存入锁记录中 友情提示:在线程内部的锁记录中会尝试cas,替换对象的Mark word,将其对象的mark word存储在锁记录中。(就是将其对象中的对象头的mark word存储到线程中的锁记录中) 3、 如果cas替换成功,那么对象的对象头储存的就是锁记录的地址和状态01,如下所示 ?4、如果cas失败,有两种情况
友情提示:如果cas失败 情况一:存在线程竞争,其他线程持有轻量级锁,将会导致锁升级,进入锁膨胀阶段 情况二:如果是自己线程执行加锁,那么将会再加一条锁记录作为重入的计入(这也体现synchronized是一个可重入锁) ?5、当线程退出synchronized代码块的时候,如果获取的是取值为 null 的锁记录 ,表示有重入,这时重置锁记录,表示重入计数减一 ?6、当线程退出synchronized代码块的时候,如果获取的锁记录取值不为 null,那么使用cas将Mark Word的值恢复给对象
锁膨胀如果在尝试加轻量级锁的过程中,cas操作无法成功,这是有一种情况就是其它线程已经为这个对象加上了轻量级锁,这是就要进行锁膨胀,将轻量级锁变成重量级锁。
2、这时 Thread-1 加轻量级锁失败,进入锁膨胀流程
?3、当Thread-0 推出synchronized同步块时,使用cas将Mark Word的值恢复给对象头,失败,那么会进入重量级锁的解锁过程,即按照Monitor的地址找到Monitor对象,将Owner设置为null,唤醒EntryList 中的Thread-1线程 自旋优化(只适合多核CPU)重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即在自旋的时候持锁的线程释放了锁),那么当前线程就可以不用进行上下文切换就获得了锁,下面的是介绍两种情况自旋重试成功和自旋重试失败 1、自旋重试成功的情况 2、自旋重试失败的情况,自旋了一定次数还是没有等到持锁的线程释放锁 ?自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。Java 7 之后不能控制是否开启自旋功能 偏向锁在轻量级的锁中,我们可以发现,如果同一个线程对同一个2对象进行重入锁时,也需要执行CAS操作,这是有点耗时,那么java6开始引入了偏向锁,只有第一次使用CAS时将对象的Mark Word头设置为入锁线程ID,之后这个入锁线程再进行重入锁时,发现线程ID是自己的,那么就不用再进行CAS了 ?偏向状态友情提示:偏向锁状态默认是延迟的 一个对象的创建过程
输出结果如下,三次输出的状态码都为101
测试禁用:如果没有开启偏向锁,那么对象创建后最后三位的值为001,这时候它的hashcode,age都为0,hashcode是第一次用到
?撤销偏向锁方式一:hashcode方法,方式二:其他线程也想得到偏向锁 ,方式二: 调用 wait/notify 撤销偏向锁-hashcode方法测试
输出结果
撤销偏向锁-其它线程使用对象这里我们演示的是偏向锁撤销变成轻量级锁的过程,那么就得满足轻量级锁的使用条件,就是没有线程对同一个对象进行锁竞争,我们使用
友情提示:偏向锁失效转换成轻量级锁
撤销 - 调用 wait/notify会使对象的锁变成重量级锁,因为wait/notify方法之后重量级锁才支持 批量重偏向如果对象被多个线程访问,但是没有竞争,这时候偏向了线程一的对象又有机会重新偏向线程二,即可以不用升级为轻量级锁,可这和我们之前做的实验矛盾了呀,其实要实现重新偏向是要有条件的:就是超过20对象对同一个线程如线程一撤销偏向时,那么第20个及以后的对象才可以将撤销对线程一的偏向这个动作变为将第20个及以后的对象偏向线程二。 友情提示:就是重新偏向到另一个线程。临界值是20,就是对应线程一撤销偏向锁20次,就会转向其他线程获得偏向锁 批量撤销锁消除和锁粗化锁粗化锁粗化的概念应该比较好理解,就是将多次连接在一起的加锁、解锁操作合并为一次,将多个连续的锁扩展成一个范围更大的锁。
? 这里每次调用stringBuffer.append方法都需要加锁和解锁,如果虚拟机检测到有一系列连串的对同一个对象加锁和解锁操作,就会将其合并成一次范围更大的加锁和解锁操作,即在第一次append方法时进行加锁,最后一次append方法结束后进行解锁。 友情提示:锁粗化说白了,就是将多个较小范围的锁,变成一个大范围的锁。 消除锁消除锁是虚拟机另外一种锁的优化,这种优化更彻底,Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间,如下StringBuffer的append是一个同步方法,但是在add方法中的StringBuffer属于一个局部变量,并且不会被其他线程所使用,因此StringBuffer不可能存在共享资源竞争的情景,所以其实这过程是线程安全的,JVM会自动将其锁消除。
?友情提示:说白了,就是将其锁去掉。 ? |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 16:58:16- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |