CPU|java面试——常见的锁和锁升级( 六 )
- 检测Mark Word里面是不是当前线程的ID , 如果是 , 表示当前线程处于偏向锁
- 如果不是 , 则使用CAS将当前线程的ID替换Mard Word , 如果成功则表示当前线程获得偏向锁 , 置偏向标志位1
- 如果失败 , 则说明有锁竞争 , 撤销偏向锁 , 进而升级为轻量级锁 。
- 当前线程使用CAS将对象头的Mark Word替换为当前线程的lock record , 如果成功 , 当前线程获得锁, 如果失败 , 则自旋不停的获取锁 。
- 当自旋一定次数之后获取成功了 , 还是用轻量级锁 , 如果失败了 , 锁再次升级为重量级锁 , 之前自旋的线程进入wait状态 , 等待cpu分配时间片后再次执行
如果线程争用激烈 , 那么应该禁用偏向锁 。
总结:对于不可能共享的资源 , 比如局部变量 , 在执行的时候jvm会把对象锁消除 , 比如一个方法里面的stringBuffer的append()
锁粗话
总结:假如有一个循环 , 循环内的操作需要加锁 , 我们应该把锁放到循环外面 , 否则每次进出循环 , 都进出一次临界区 , 效率是非常差的
消除缓存行的伪共享除了我们在代码中使用的同步锁和jvm自己内置的同步锁外 , 还有一种隐藏的锁就是缓存行 , 它也被称为性能杀手 。在多核cup的处理器中 , 每个cup都有自己独占的一级缓存、二级缓存 , 甚至还有一个共享的三级缓存 , 为了提高性能 , cpu读写数据是以缓存行为最小单元读写的;32位的cpu缓存行为32字节 , 64位cup的缓存行为64字节 , 这就导致了一些问题 。例如 , 多个不需要同步的变量因为存储在连续的32字节或64字节里面 , 当需要其中的一个变量时 , 就将它们作为一个缓存行一起加载到某个cup-1私有的缓存中(虽然只需要一个变量 , 但是cpu读取会以缓存行为最小单位 , 将其相邻的变量一起读入) , 被读入cpu缓存的变量相当于是对主内存变量的一个拷贝 , 也相当于变相的将在同一个缓存行中的几个变量加了一把锁 , 这个缓存行中任何一个变量发生了变化 , 当cup-2需要读取这个缓存行时 , 就需要先将cup-1中被改变了的整个缓存行更新回主存(即使其它变量没有更改) , 然后cup-2才能够读取 , 而cup-2可能需要更改这个缓存行的变量与cpu-1已经更改的缓存行中的变量是不一样的 , 所以这相当于给几个毫不相关的变量加了一把同步锁; 为了防止伪共享 , 不同jdk版本实现方式是不一样的: \\1. 在jdk1.7之前会 将需要独占缓存行的变量前后添加一组long类型的变量 , 依靠这些无意义的数组的填充做到一个变量自己独占一个缓存行; \\2. 在jdk1.7因为jvm会将这些没有用到的变量优化掉 , 所以采用继承一个声明了好多long变量的类的方式来实现; \\3. 在jdk1.8中通过添加sun.misc.Contended注解来解决这个问题 , 若要使该注解有效必须在jvm中添加以下参数: -XX:-RestrictContended
sun.misc.Contended注解会在变量前面添加128字节的padding将当前变量与其他变量进行隔离;
【CPU|java面试——常见的锁和锁升级】参考资料:https://www.cnblogs.com/linghu-java/p/8944784.html
- Java基础知识回顾,还记得吗?
- mybatis sharding-jdbc Java8日期
- 树莓派控制步进电机-TB6600-Java版本
- 德州点创教育JavaScript正则表达式授课大纲
- 如何编写JAVA小白第一个程序
- 英特尔推出新款四核CPU,目标直指Ryzen3 3300X
- 入门到老手全覆盖,锐龙CPU给3D创作加速
- java安全编码指南之:异常简介
- Java学习路线图
- Java核心技术点有哪些 有没有什么书籍推荐