纸上得来终觉浅->代码详解锁升级

        背景

        最近再看关于锁升级的内容,一方面这个是编写代码时程序性能提高的一个利器,另一方面这部分也会是面试时候的热门话题。那么作者最开始也是通过b站视频包括一些csdn上面的资料去看,最终发现只是有一些结论,而没有具体的例子。这篇文章里一切都是以jdk8为环境基础展开。

        一种观点是:锁在没有竞争的情况下会是偏向锁,在遇到竞争的时候会升级成轻量级锁,再遇到竞争了这个锁会进行一定次数的自旋,当次数计满了就会升级成为重量级锁。

        另一种观点是:无论是轻量级锁还是偏向锁在遇到竞争的时候都会升级成重量级锁,而重量级锁会通过自旋的操作来避免阻塞的发生来提高性能,并且自旋也是自适应的。

        值得一提的是,在jdk8中,偏向锁的开启是有延迟的,并不是一开启的时候就是偏向锁,而自旋锁是一开始就有的。

        下面我们就围绕着代码和实际情况来看偏向锁,轻量级锁,重量级锁,以及偏向锁的重偏向和撤销的过程。

        代码

        这里先把测试的代码给大家,根据满叔的资料进行改编。大家在观察代码的时候要注意两个线程是否发生了竞争,包括这个日志打印的顺序是怎么样的。

package com.zzm.juc.syn;import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.ClassLayout;import java.util.Vector;
import java.util.concurrent.locks.LockSupport;/*** @BelongsProject: happystudy* @BelongsPackage: com.zzm.juc.syn* @Author: zzm* @CreateTime: 2024-03-27  11:23* @Description: TODO* @Version: 1.0*/@Slf4j(topic = "c.SyncWaitNotify")
public class SyncWaitNotify {private int flag;private int loopNumber;static Thread t1,t2,t3;private final Object lock = new Object();public SyncWaitNotify(int flag, int loopNumber) {this.flag = flag;this.loopNumber = loopNumber;}public void print(int waitFlag, int nextFlag, String str) {log.debug("对象头" + ClassLayout.parseInstance(lock).toPrintable());for (int i = 0; i < loopNumber; i++) {synchronized (this) {while (this.flag != waitFlag) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print(str);flag = nextFlag;log.debug("对象头" + ClassLayout.parseInstance(lock).toPrintable());this.notifyAll();}}}private static void test4() throws InterruptedException {Vector<Dog> list = new Vector<>();int loopNumber = 39;t1 = new Thread(() -> {for (int i = 0; i < loopNumber; i++) {Dog d = new Dog();list.add(d);synchronized (d) {log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());}}LockSupport.unpark(t2);}, "t1");t1.start();t2 = new Thread(() -> {LockSupport.park();log.debug("===============> ");for (int i = 0; i < loopNumber; i++) {Dog d = list.get(i);log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());synchronized (d) {log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());}log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());}LockSupport.unpark(t3);}, "t2");t2.start();t3 = new Thread(() -> {LockSupport.park();log.debug("===============> ");for (int i = 0; i < loopNumber; i++) {Dog d = list.get(i);log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());synchronized (d) {log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());}log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());}}, "t3");t3.start();t3.join();Dog dd = new Dog();log.debug(ClassLayout.parseInstance(dd).toPrintable());for(int i=0;i<2;i++){synchronized (dd) {log.debug("不可偏向"+ClassLayout.parseInstance(dd).toPrintable());}}log.debug(ClassLayout.parseInstance(dd).toPrintable());}private static void test3() throws InterruptedException {Vector<Dog> list = new Vector<>();Thread t1 = new Thread(() -> {for (int i = 0; i < 30; i++) {Dog d = new Dog();list.add(d);synchronized (d) {log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());}}synchronized (list) {list.notify();}}, "t1");t1.start();Thread t2 = new Thread(() -> {synchronized (list) {try {list.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("===============> ");for (int i = 0; i < 30; i++) {Dog d = list.get(i);log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());synchronized (d) {log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());}log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable());}}, "t2");t2.start();}private static void test2() throws InterruptedException {Dog d = new Dog();Thread t1 = new Thread(() -> {synchronized (d) {log.debug(ClassLayout.parseInstance(d).toPrintable());}synchronized (TestBiased.class) {TestBiased.class.notify();}}, "t1");t1.start();Thread t2 = new Thread(() -> {synchronized (TestBiased.class) {try {TestBiased.class.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug(ClassLayout.parseInstance(d).toPrintable());synchronized (d) {log.debug(ClassLayout.parseInstance(d).toPrintable());}log.debug(ClassLayout.parseInstance(d).toPrintable());}, "t2");t2.start();}public static void main(String[] args) throws InterruptedException {
//        SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 2);
//        new Thread(() -> {
//            syncWaitNotify.print(1, 2, "a");
//        }).start();
//        new Thread(() -> {
//            syncWaitNotify.print(2, 3, "b");
//        }).start();
//        new Thread(() -> {
//            syncWaitNotify.print(3, 1, "c");
//        }).start();test4();
//        test3();
//        test2();}}

markword阅读:

这里的toPrintable读出来的字符串,前两行是markword,并且要倒着读也就是从后往前读,这个详细了解可以去搜索一下这个classlayout的相关博客是如何查看对象头信息的。

锁升级

比如说这段代码

t1,t2没有竞争,打印出来头信息最后两位为0

那么全程这个就是轻量级锁,因为偏向锁是有延迟的,所以我们加一个参数

 

这个对象加锁后就会变成偏向锁

 

带偏向锁的情况下 发生了竞争

 

可以看到一开始是偏向锁 

 

 最后变成了10,直接成为了重量级锁跳过了轻量级。而经过测试轻量级锁发生竞争也会立刻变成重量级锁。

  test3->重偏向

          一个对象,关闭偏向锁延迟,上来他是偏向锁,这个时候两个线程不会竞争但是会交替运行,那么他会由偏向锁,变成轻量级锁,现在准备30个对象,把他放到list里面,还是两个线程没有竞争的情况,t1线程对list里面对象加锁,都会变成偏向锁,在加锁的时候。

        这个时候t1执行完毕到t2了,t2又把这list里面的30个对象依次取出来,进行加锁,偏向锁的重偏向是有阈值的,这里为20。那么在t2线程里面拿出的前20个对象,他的轻量级锁的线程id都是t1,他们都会从偏向锁变成轻量级锁。

但是超过20次了,

 

 

第21个jvm可能就会认为这个对象对于t1线程是不是偏向错了,因此他把后面还没来得及遍历的10个对象,一起把偏向锁的线程id变成了t2线程。

test4->批量撤销

        但是同理,假设这个时候对象变成了40个,t1对每个对象加锁完之后,t2右对他们加锁,t2加锁完之后,t3又对他们进行加锁的重偏向,

当阈值达到40的时候,jvm可能就会认为这个类下的对象竞争比较激烈就不允许给这个类下的对象设置偏向锁了,再创建一个,加锁的时候也就变成了轻量级锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/778802.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

​python学习之变量类型​

print单纯输中的十种数据类型只需要用print()函数即可&#xff0c;()里面直接写变量名。 下面重点介绍print格式输出&#xff1a; 第一种方法&#xff1a;一个萝卜一个坑&#xff0c;下面的代码中&#xff0c;{0}、{1}、{2}分别表示j,i,j*i&#xff0c;单引号里面是输出格式。…

什么是土壤墒情检测站?它在农业生产中有什么作用?

土壤墒情检测站是一种专门用于监测土壤水分状况和土壤水力性质的设备。它由多个传感器和数据采集单元组成&#xff0c;能够实时监测土壤中的水分含量、土壤温度等参数&#xff0c;并收集和记录相关的数据&#xff0c;提供土壤墒情&#xff08;即土壤水分状态&#xff09;的详细…

【学习】软件测试人员如何设计出优秀的测试用例

在软件开发的过程中&#xff0c;测试用例如同工程质量的守护者&#xff0c;它们的存在确保了软件产品的稳定性和可靠性。然而&#xff0c;如何设计出优秀的测试用例&#xff0c;让其在千变万化的软件世界中独领风骚&#xff0c;成为众多测试工程师追寻的目标。本文将为你揭示其…

Android RecyclerView 滑动后选中的条目居中显示

话不多说先看效果: 实录效果视频如下 滚动居中 RecyclerView 在原有的RecyclerView 基础上操作&#xff0c;其他步骤不变&#xff0c;只是替换一下 manager 步骤 导入依赖 maven { url https://www.jitpack.io }//无限滚动implementation com.github.ZhaoChanghu:GalleryLayou…

1、Cocos Creator 基础入门

目录 Cocos Creator 是什么&#xff1f; 语言支持 功能特性 工作流程 功能模块 相关游戏 参考 Cocos Creator 是什么&#xff1f; Cocos Creator 既是一款高效、轻量、免费开源的跨平台 2D&3D 图形引擎&#xff0c;也是一个实时 2D&3D 数字内容创作平台。拥有…

Java开发过程中如何进行进制换换

最近由于工作上的需要&#xff0c;遇到进制转换的问题。涉及到的进制主要是十进制、十六进制、二进制转换。 1、十进制转十六进制、二进制 调用java自带的api,测试十进制转16进制、2进制 package com.kangning.common.utils.reflect;/*** 十进制 转 十六进制* 十进制 转 二进…

机器学习算法的另一个分支-贝叶斯算法原理(贝叶斯要解决什么问题)

目录 一、贝叶斯简介 二、贝叶斯要解决的问题 三、例子&#xff08;公式推导&#xff09; 四、实例 1. 拼写纠正实例 2. 垃圾邮件过滤实例 一、贝叶斯简介 1. 贝叶斯&#xff1a;英国数学家。1702年出生于伦敦&#xff0c;做过神甫。贝叶斯在数学方面主要研究概率论.对于…

初识C++ · 入门(1)

目录 前言&#xff1a; 1 命名空间 2 输入和输出 3 缺省参数 5 函数重载 前言&#xff1a; C与C语言是有一定交集的&#xff0c;可以理解为本贾尼在使用C语言的时候认为有缺陷&#xff0c;于是加了一些小语法进行改良&#xff0c;后来经过委员会的修改&#xff0c;C98问世…

深度学习故障诊断实战 | 数据预处理之基于滑动窗的数据样本增强

前言 本期给大家分享介绍如何基于滑动窗方法进行数据样本增强 背景 深度学习模型训练需要大量的样本。在故障诊断领域&#xff0c;每个类别大都会达到300个样本。但是在实际公开数据集中&#xff0c;以CWRU数据集为例&#xff0c;每个类别只有24组数据&#xff0c;这明显是不…

CSGO赛事管理系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目持续更新中..... 2024年计算机毕业论文&#xff08;设计&#xff09;学生选题参考合集推荐收藏&#xff08;包含Springboot、jsp、ssmvue等技术项目合集&#xff09; 目录 1. 系…

ExpandableNotificationRow的父类layout

ExpandableNotificationRow的父类layout NotificationsQuickSettingsContainer NotificationPanelView NotificationShadeWindowView ExpandableNotificationRow 就是下图的一个 Notification&#xff1a; USB连接 。 何时创建一个ExpandableNotificationRow 并且被添加到Not…

Spring Boot 工程开发常见问题解决方案,日常开发全覆盖

本文是 SpringBoot 开发的干货集中营&#xff0c;涵盖了日常开发中遇到的诸多问题&#xff0c;通篇着重讲解如何快速解决问题&#xff0c;部分重点问题会讲解原理&#xff0c;以及为什么要这样做。便于大家快速处理实践中经常遇到的小问题&#xff0c;既方便自己也方便他人&…

护眼台灯对眼睛有危害吗?多款预防近视的台灯推荐

在日常生活中&#xff0c;灯光对于我们而言&#xff0c;是非常重要的&#xff0c;尤其是在夜晚&#xff0c;不管是学习还是办公都需要合适的光线环境。很多家长为了保护孩子的视力会选择从台灯下手&#xff0c;但又不知道护眼台灯对眼睛有危害吗&#xff1f;今天就来好好的告诉…

【MySQL】数据库--表操作

目录 一、创建表 二、查看表 三、修改表 1. 添加字段--add 2.修改表名--rename to 3.修改列名--change 4.修改字段的数据类型--modify 5.删除字段&#xff08;列&#xff09;--drop 四、删除表 一、创建表 create [temporary]table[if not exists]table_name [([colu…

python如何获取word文档的总页数

最近在搞AI. 遇到了一个问题&#xff0c;就是要进行doc文档的解析。并且需要展示每个文档的总页数。 利用AI. 分别尝试了chatGPT, 文心一言&#xff0c; github copilot&#xff0c;Kimi 等工具&#xff0c;给出来的答案都不尽如人意。 给的最多的查询方式就是下面这种。 这个…

【Canvas与艺术】硬朗风格十二棱表表盘

【效果图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>硬朗风格十二棱表表盘</title><style type"text/css…

Qt+OpenGL入门教程(三)——绘制三角形

通过前两篇文章的学习&#xff0c;我想大家应该有了基本的理解&#xff0c;我们接下来实操一下。 创建Qt OpenGL窗口 QOpenGLWidget QGLWidget是传统QtOpenGL模块的一部分&#xff0c;与其他QGL类一样&#xff0c;应该在新的应用程序中避免使用。相反&#xff0c;从Qt5.4开始…

新手如何用Postman做接口自动化测试?

1、什么是自动化测试 把人对软件的测试行为转化为由机器执行测试行为的一种实践。 例如GUI自动化测试&#xff0c;模拟人去操作软件界面&#xff0c;把人从简单重复的劳动中解放出来&#xff0c;本质是用代码去测试另一段代码&#xff0c;属于一种软件开发工作&#xff0c;已…

【检索增强】Retrieval-Augmented Generation for Large Language Models:A Survey

本文简介 1、对最先进水平RAG进行了全面和系统的回顾&#xff0c;通过包括朴素RAG、高级RAG和模块化RAG在内的范式描述了它的演变。这篇综述的背景下&#xff0c;更广泛的范围内的法学硕士研究RAG的景观。 2、确定并讨论了RAG过程中不可或缺的核心技术&#xff0c;特别关注“…

深入Facebook的世界:探索数字化社交的无限可能性

引言 随着数字化时代的到来&#xff0c;社交媒体平台已经成为了人们日常生活中不可或缺的一部分&#xff0c;而其中最为突出的代表之一便是Facebook。作为全球最大的社交媒体平台之一&#xff0c;Facebook不仅仅是一个社交网络&#xff0c;更是一个数字化社交的生态系统&#…