Java synchronized锁升级jol过程详解
作者:Katsu 发布时间:2023-04-15 04:58:51
jol(java object layout)需要的依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
一。synchronized锁对象的升级(膨胀)过程主要如下:
1.膨胀过程:无锁(锁对象初始化时)-> 偏向锁(有线程请求锁) -> 轻量级锁(多线程轻度竞争)-> 重量级锁(线程过多或长耗时操作,线程自旋过度消耗cpu);
2.jvm默认延时4s自动开启偏向锁(此时为匿名偏向锁,不指向任务线程),可通过-XX:BiasedLockingStartUpDelay=0取消延时;如果不要偏向锁,可通过-XX:-UseBiasedLocking = false来设置
3.锁只能升级,不能降级;偏向锁可以被重置为无锁状态
4.锁对象头记录占用锁的线程信息,但不能主动释放,线程栈同时记录锁的使用信息,当有其他线程(T1)申请已经被占用的锁时,先根据锁对向的信息,找对应线程栈,若线程已结束,则锁对象先被置为无锁状态,再被T1线程占有后置为偏向锁;若线程位结束,则锁状态由当前偏向锁升级为轻量级锁。
5.偏向锁和轻量级锁在用户态维护,重量级锁需要切换到内核态(os)进行维护;
二。锁对象头(markword部分,8字节)使用不同的状态进行表示,64位虚拟机的markword如下所示:
使用jol演示如下:
1.无锁状态
Object object = new Object(); System.out.println("hash: " + object.hashCode()); System.out.println(ClassLayout.parseInstance(object).toPrintable());
header中前8个字节按照平时习惯的从高位到低位的展示为:00000000 00000000 00000000 00111001 10101110 11101101 00101111 00000001
对照上图,最后3位是001,无锁状态,中间31位(0111001 10101110 11101101 00101111)换算成十进制即为上图打印的hash:967765295
2.匿名偏向锁和偏向锁
Thread.sleep(5000); //等待jvm开启偏向锁
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
第一次打印为匿名偏向,第二次偏向锁指向了main线程
注意:用run启动程序,不要用debug,实验的时候,用debug启动,第二次打印直接升级轻量级锁。
3.轻量级锁
public static void main(String[] args) throws InterruptedException {
Thread.sleep(5000);
Object o = new Object();
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
for (int i = 0; i < 1; i++) {
Thread t = new Thread(() -> {
print(o);
});
t.start();
}
}
public static void print(Object o) {
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
4.重量级锁
public static void main(String[] args){
Object o = new Object();
for (int i = 0; i < 2; i++) {
Thread t = new Thread(() -> {
print(o);
});
t.start();
}
}
public static void print(Object o) {
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
来源:https://www.cnblogs.com/katsu2017/p/12610002.html


猜你喜欢
- ForkJoinTask就是ForkJoinPool里面的每一个任务。他主要有两个子类:RecursiveAction和RecursiveT
- 一.以springboot为例,建立代码1.IExecCommandServer:public interface IExecCommand
- 本文实例讲述了C#实现让ListBox适应最大Item宽度的方法。分享给大家供大家参考。具体实现方法如下:private void butt
- 前言去重,对于很多场合必不可少。写此篇文章是因为在之前做某个画面中,我在数据库中进行 Distinct 和 Order By 去重,发现影响
- 本文实例为大家分享了C#根据http和ftp地址获取对应图片的具体代码,供大家参考,具体内容如下public class GetBitmap
- 本文实例为大家分享了Java实现银行ATM系统的具体代码,供大家参考,具体内容如下一、前言银行ATM系列简单操作二、使用步骤1.创建用户信息
- 简单工厂模式概述1.定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类2.在简单工厂模式中用于被创
- bootstrap.yml和bootstrap.properties优先级直接先说结论 bootstrap.properties 优于boo
- 简介AccessibilityService的设计初衷是为了辅助有身体缺陷的群体使用Android应用,它的设计贯穿着Android的控件树
- 在开发过程中,碰到生成一个List对象,需要对其里面的每个对象都进行校验。但是,这个Lis
- 本文实例为大家分享了java实现打字游戏的具体代码,供大家参考,具体内容如下import java.util.Random;import j
- Spring框架提供了事务管理的标准实现,且可以通过注解或者XML文件的方式声明和配置事务。通过异步事件的方式解耦服务调用,可以提高程序的响
- 前言有时候我们会在属性注入的时候添加@Lazy注解实现延迟注入,今天咱们通过阅读源码来分析下原因一、一个简单的小例子代码如下:@Servic
- 一、实现接口调用一个接口中的方法,传统方法:接口类A:package lombda;/** * @author yeqv * @progra
- 常量池Java中我们创建String对象有两种基本方法。String str1 = "zxhtom";String st
- 前言大家应该都用过synchronized 关键字加锁,用来保证某个时刻只允许一个线程运行。那么如果控制某个时刻允许指定数量的线程执行,有什
- 一. 类型转型将一个类型转换成另一个类型的过程被称为类型转换。 我们所说的对象类型转换,一般是指两个存在继承关系的对象,而不是任意类型的对象
- 在源码的阅读过程中,可以了解别人实现某个功能的涉及思路,看看他们是怎么想,怎么做的。接下来,我们看看这篇Java源码解析之object的详细
- Android Studio 在引用外部依赖时,发现一直无法引用外部依赖。刚开始以为是墙的问题,尝试修改Gradle配置,未解决问题。最终发
- SpringBoot接口开发服务端@RestController@RequestMapping("/landary")p