锁的升级过程(代码演示)

news/2024/10/23 5:51:34/

1 前言

        锁的状态有4种,无锁偏向锁轻量级锁重量级锁。那为什么会有锁升级的这种概念,其实大家都知道synchronized 在1.6之后做了升级,但具体是升级了什么?其实在jdk1.6之前锁只有重量级锁这个概念(但是重量级锁需要向内核申请额外的锁资源,涉及到用户态和内核态的切换,比较浪费资源)。升级了之后就不需要直接从无锁到重量级锁了,所以做了锁升级。

        偏向锁:当只有一个线程争抢锁资源的时候,将线程拥有者标示为当前线程。

        轻量级锁(自旋锁):一个或多个线程通过CAS去争抢锁,如果抢不到就一直自旋,知道超过自旋次数,升级为重量级锁。

        重量级锁:多个线程争抢锁,向内核申请锁资源,将为抢到的锁放在队列中直接阻塞。

锁升级过程:

1 当只有一个线程a去竞争资源时,会先使用偏向锁,就是给一个标识,说明这个锁正在被线程a占有。

2 当线程a获取锁之后,线程b、线程c又来竞争锁,发现锁被线程a获取,于是撤销偏向锁的标识,线程a、b、c开始通过CAS去竞争锁

3 当其中一个线程获取锁之后(假如是a),然后线程b和线程c一直自旋获取锁,等到自旋到一定次数依然没有获取锁时,就将锁升级为重量级锁,向内核升级资源,直接将等待的线程阻塞。

2 对象在内存中的布局

对象头在主要包括两部分数据:Mark Word(标记字段)和 Klass Pointer(类型指针)

Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。这些信息都是与对象自身定义无关的数据,所以Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。

Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

其实synchronized 用的锁是存在对象头里的,具体是存在对象头的Mark Word中。那Mark Word在对象头中存储了什么呢?

64位的虚拟机:

在这里插入图片描述

32位的虚拟机:

在这里插入图片描述

 可以发现锁标志位分别是:无锁(01)偏向锁(01)轻量级锁(00),重量级锁(10)

3 代码演示锁升级过程

引入依赖:

        <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version></dependency>

测试代码:

public class TestB {private byte test = 1;public static void main(String[] args) {TestB testB = new TestB();System.out.println(ClassLayout.parseInstance(testB).toPrintable());}}

测试结果:

序号1:请求头mark Word占8个字节

序号2:请求头Klass Pointer占4个字节

序号3:成员变量信息,一个byte类型变量占一个字节,多出来的3个字节间隙填充补齐(alignment/padding gap)

序号4:成员变量信息,一个string类型占4个字节

序号5:补充4个字节变成8的倍数。

锁升级过程代码:

public class TestB {static Object object = new Object();public static class LockThread implements Runnable{@Overridepublic void run() {synchronized (object){//进行一些耗时操作String test="";for (int i = 0; i < 1000; i++) {test += "2";}}}}public static void main(String[] args) {//只有一个线程去抢占锁new Thread(new LockThread()).start();System.out.println("===========偏向锁===========");System.out.println(ClassLayout.parseInstance(object).toPrintable());//再加一个线程,两个线程去抢占锁new Thread(new LockThread()).start();System.out.println("===========轻量级锁===========");System.out.println(ClassLayout.parseInstance(object).toPrintable());//多个线程抢占,一直在自旋,升级位重量级锁for (int i = 0; i < 20; i++) {new Thread(new LockThread()).start();}System.out.println("===========重量级锁===========");System.out.println(ClassLayout.parseInstance(object).toPrintable());}}

结果:


http://www.ppmy.cn/news/1037843.html

相关文章

【高阶数据结构】红黑树详解

文章目录 前言1. 红黑树的概念及性质1.1 红黑树的概念1.2 红黑树的性质1.3 已经学了AVL树&#xff0c;为啥还要学红黑树 2. 红黑树结构的定义3. 插入&#xff08;仅仅是插入过程&#xff09;4. 插入结点之后根据情况进行相应调整4.1 cur为红&#xff0c;p为红&#xff0c;g为黑…

rust踩雷笔记(2)——一道hard带来的思考[哈希表、字符串、滑动窗口]

今天被一道hard恶心坏了&#xff0c;算法不难&#xff0c;用C几分钟的事。用rust主要还是缺乏对语言的熟练度&#xff0c;记录一下&#xff0c;主要的坑在下面这个操作&#xff1a; 对String取其中某个位置的char。 可能你会有疑问&#xff1a;这不是直接nth()取就行了么。没错…

二刷LeetCode--148. 排序链表(C++版本),必会题,思维题

思路&#xff0c;本题其实考察了两个点&#xff1a;合并链表、链表切分。首先从1开始&#xff0c;将链表切成一段一段&#xff0c;因为需要使用归并&#xff0c;所以下一次的切分长度应该是当前切分长度的二倍&#xff0c;每次切分&#xff0c;我们拿出两段&#xff0c;然后将第…

Jetpack 中的 LiveData 粘性事件

什么叫粘性事件 LiveData使用篇Jetpack 中的 LiveData - 使用篇_简单不一定不好的博客-CSDN博客后再进一步了解LiveData。观察者和被观察者&#xff0c;正常情况下观察者先注册&#xff0c;被观察者再发送观察事件&#xff1b;所以粘性事件可以理解为观察者模式的升级&#xf…

LeetCode235. 二叉搜索树的最近公共祖先

235. 二叉搜索树的最近公共祖先 文章目录 [235. 二叉搜索树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/)一、题目二、题解方法一&#xff1a;递归方法二&#xff1a;迭代 一、题目 给定一个二叉搜索树, 找到该树中两个指定…

ZooKeeper的应用场景(集群管理、Master选举)

5 集群管理 随着分布式系统规模的日益扩大&#xff0c;集群中的机器规模也随之变大&#xff0c;因此&#xff0c;如何更好地进行集群管理也显得越来越重要了。 所谓集群管理&#xff0c;包括集群监控与集群控制两大块&#xff0c;前者侧重对集群运行时状态的收集&#xff0c;后…

uniapp 上传比较大的视频文件就超时

uni.uploadFile&#xff0c;上传超过10兆左右的文件就报错err&#xff1a;uploadFile:fail timeout&#xff0c;超时 解决&#xff1a; 在manifest.json文件中做超时配置 uni.uploadFile({url: this.action,method: "POST",header: {Authorization: uni.getStorage…

IDEA部署配置Maven项目教程,IDEA配置Tomcat(2019.3.3)(2023.1.3)

我们往往会用到多版本的IDEA进行一个Maven项目配置部署&#xff0c;还有tomcat的配置&#xff0c;这里就有你需要的&#xff0c;有低版本的&#xff0c;也有高版本的&#xff0c;根据自己的情况来进行一个操作 一、前言 当涉及到软件开发和项目管理时&#xff0c;使用一个可靠的…