锁的分类,以及锁升级原理

news2025/1/17 8:54:01

1. 前言

在并发编程中非常重要,但是锁的种类有点多。这边文章的目的就是为了梳理的分类以及 锁升级的原理。

2. 锁的分类

种类\名称synchronizedReentrantLockReentrantReadWriteLock
可重入锁
不可重入锁×××
乐观锁
悲观锁
公平锁×
非公平锁
互斥锁
共享锁××

①:Java中提供的CAS操作,就是乐观锁的一种实现。其余的锁的形式都是悲观锁

  • 重入锁:当前线程获取到A锁,在获取之后尝试再次获取A锁是可以直接拿到的
    public static void add() {
        synchronized (T11_Thread_Lock01.class) {
            synchronized (T11_Thread_Lock01.class) {

            }
        }
    }
  • 不可重入锁:当前线程获取到A锁,在获取之后尝试再次获取A锁,无法获取到的,因为A锁被当前线程占用着,需要等待自己释放锁再获取锁
  • 悲观锁:获取不到锁资源时,会将当前线程挂起(进入BLOCKEDWAITING),线程挂起会涉及到用户态和内核的太的切换,而这种切换是比较消耗资源的
  • 乐观锁:获取不到锁资源,可以再次让CPU调度,重新尝试获取锁资源,Atomic原子性类中,就是基于CAS乐观锁实现的
  • 公平锁:线程A获取到了锁资源,线程B没有拿到,线程B去排队,线程C来了,锁被A持有,同时线程B在排队。直接排到B的后面,等待B拿到锁资源或者是B取消后,才可以尝试去竞争锁资源。一句话:排队顺序,一个一个来
  • 非公平锁:线程A获取到了锁资源,线程B没有拿到,线程B去排队,线程C来了,先尝试竞争一波
    • 拿到锁资源:开心,插队成功
    • 没有拿到锁资源:依然要排到B的后面,等待B拿到锁资源或者是B取消后,才可以尝试去竞争锁资源
  • 互斥锁:同一时间点,只会有一个线程持有者当前互斥锁
  • 共享锁:同一时间点,当前共享锁可以被多个线程同时持有

3. 类锁以及对象锁

  1. synchronized的使用一般就是同步方法和同步代码块。
  2. synchronized的锁是基于对象实现的。
class Test {
    // 此方法锁定的是Test.class
    public static synchronized void a() {

    }

    // 此方法锁定的是this
    public synchronized void b() {

    }
}
  • static:此时使用的是当前类.class 作为锁(类锁)
  • 非static:此时使用的是当前对象作为锁(对象锁)

4. synchronized的优化

在JDK1.5的时候,Doug Lee推出了ReentrantLock,lock的性能远高于synchronized,所以JDK团队就在JDK1.6中,对synchronized做了大量的优化

4.1 锁消除

在synchronized修饰的代码中,如果不存在操作临界资源的情况,会触发锁消除,你即便写了synchronized,他也不会触发

public class T13_Thread_Lock03 {
    public synchronized static void add() {
        // 方法内无任何临界操作。此时synchronized是可有可无的。 依赖于锁消除
    }
}

4.2 锁膨胀

如果在一个循环中,频繁的获取和释放做资源,这样带来的消耗很大,锁膨胀就是将锁的范围扩大,避免频繁的竞争和获取锁资源带来不必要的消耗

public class T14_Thread_Lock04 {
    private static boolean flag = true;
    public static void add() {
        while (flag) {
            // 此处不停的创建锁 以及释放锁资源
            synchronized (T14_Thread_Lock04.class) {
            }
        }
    }
}

4.3 synchronized实现原理

synchronized是基于对象实现的。

先要对Java中对象在堆内存的存储有一个了解。

image.png

展开MarkWord

MarkWord中标记着四种锁的信息:无锁、偏向锁、轻量级锁、

4.4 锁升级

  • 锁默认情况下,开启了偏向锁延迟
    • 偏向锁在升级为轻量级锁时,会涉及到偏向锁撤销,需要等到一个安全点(STW),才可以做偏向锁撤销,在明知道有并发情况,就可以选择不开启偏向锁,或者是设置偏向锁延迟开启
    • 因为JVM在启动时,需要加载大量的.class文件到内存中,这个操作会涉及到synchronized的使用,为了避免出现偏向锁撤销操作,JVM启动初期,有一个延迟4s开启偏向锁的操作
    • 如果正常开启偏向锁了,那么不会出现无锁状态,对象会直接变为匿名偏向

4.4.1 无锁状态 案例

public class T16_Thread_Lock06 {
    public static void main(String[] args) {
        Object o = new Object();

        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}

在这里插入图片描述

4.4.2 偏向锁 案例

public class T17_Thread_Lock07 {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);

        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}

在这里插入图片描述

4.4.3 轻量级锁 实例

public class T18_Thread_Lock08 {
    public static void main(String[] args) {
        Object o = new Object();

        synchronized (o) {
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }
}

在这里插入图片描述

4.4.4 重量级锁 案例

public class T19_Thread_Lock09 {
    public static void main(String[] args) {
        Object o = new Object();

        new Thread(() -> {
            synchronized (o) {
                System.out.println("锁竞争:" + ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();

        synchronized (o) {
            System.out.println("主线程:" + ClassLayout.parseInstance(o).toPrintable());
        }
    }
}

在这里插入图片描述

4.4.5 锁的升级过程

image.png

4.4.6 唤醒偏向锁的过程

public class T20_Thread_Lock10 {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        Object o = new Object();

        // 延迟5s之后 此对象的MarkWord 标识为 匿名偏向锁
        System.out.println(ClassLayout.parseInstance(o).toPrintable());

        synchronized (o) {
            // 此对象的MarkWord 标识为 偏向锁
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }
}

在这里插入图片描述

  • 因为本身存在偏向锁延迟 所以上述实例直接延迟5s Thread.sleep(5000);.
  • 如果关闭了偏向锁延迟的话,就没有了无锁态了。直接变为了匿名偏向锁
  • 加锁后,就是偏向锁

在这里插入图片描述

5. 总结

锁的相关的部分知识先总结到这,如果还有新的认识小编会及时的补充的,如果对大家有用的话,也希望大家及时关注收藏哦.

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

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

相关文章

javaweb-Servlet基本使用

1, Servlet 1.1 简介 Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。 使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容。 Servlet是JavaEE规范之一,其实就是一个接口&#xff0c…

关于分组函数(聚合函数)

分组函数:也叫"多行处理函数"或"聚合函数" (特点:输入多行,最终输出一行) 用于对表中指定字段下的内容进行统计的函数。 - count() 计数(返回指定字段下内容不为null的数…

一文看懂Linux内存管理:虚拟内存、用户空间、内核空间、用户态、内核态、IPC通信原理

目录 一、虚拟内存地址 1.为什么要有虚拟内存地址? 2.虚拟地址好处 二、用户空间和内核空间 1.概念 2.用户态和内核态 3.IPC通信原理 一、虚拟内存地址 1.为什么要有虚拟内存地址? 因为如果CPU直接访问物理内存,那如果两个进程写入一…

MSF客户端渗透

利用Acrobat Reader漏洞执行payload ● 构造PDF文件 exploit/windows/fileformat/adobe_utilprintf仅支持8.1.2软件版本和XP系统; ● 构造恶意网站 exploit/windows/browser/adobe_utilprintf#同理,浏览器上运行pDF● 之后可以通过Meterpreter进行下一…

克罗格 Kroger EDI需求分析及注意事项

项目背景 Kroger(美国克罗格公司)是具有百年历史的名店之一。它虽然历史悠久,但并没有被变化的世界所淘汰,相反,它围绕着市场消费需求的变化,不断地进行创新,创造了世界零售百年史上的若干个第…

Linux物理内存管理:page、zone、node

基本概念页:struct page ,如下图所示,x86架构下一般为4K为大小分区:struct zone ,如下图所示,x86架构下分为三个区 ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM内存节点:struct node。对于一个简单的嵌入…

杭州社保解读截止2023

杭州社保新政规定的社保内容以及缴费比例是怎样的? 1、养老保险:单位,14%;个人,8%。 2、医疗保险:单位,9.9%;个人,2% 。 3、失业保险:单位,0.5%;个人,0.5%。 4、工伤…

PC浏览器无法浏览网页的解决教程

前言 在浏览Potplayer官网和Github官网时,无论是火狐浏览器还是Edge浏览器,都无法正常链接到网站。以下教程不纠结具体原理,只介绍具体步骤,以便以后自查。而且以下教程以Github官网为例。 具体步骤 打开Chinese Firewall Test…

excel统计函数:应用广泛的动态统计之王OFFSET 上篇

【前言】OFFSET函数是判断Excel函数使用者是否进阶的一个重要函数之一。在实际工作中,如果你需要对工作中的数据文件进行系统化、自动化的建模,那么势必会使用这个函数。【功能及语法】OFFSET函数的功能是,以指定的引用为参照系,通…

你一定要知道这6个,高质量图片素材库

其实图片素材网站已经分享过很多次了,除了大家非常熟悉的 Pixabay、Pexels之外,其实还有很多优质的图片素材网。今天再给大家推荐几个,对你有帮助记得点赞、收藏哦! 1、潮点视频 https://shipin520.com/shipin-tp/0-1329-0-0-0-…

轻松掌握Jenkins

Jenkins一、docker安装Jenkins1.linux2.web二、流水线1.gitlab连接jenkins1.jenkins服务器创建密钥2.将密钥添加到gitlab上3.创建gitlab访问令牌4.安装jenkins插件5.配置 GitLab 凭据2.创建流水线1.新增凭证配置 Username with password2.创建任务3.构建触发器4.构建5.配置gitl…

MySQL的卸载下载安装,都一条龙服务了,你还不快来???

目录 MySQL的卸载 第一步、停止 MySQL 服务 第二步、卸载有关 MySQL 的程序 第三步、删除相关 MySQL 相关文件 第四步、删除 MySQL 残留文件 MySQL的下载 MySQL的安装 由于我的 MySQL 之前安装的版本太低,导致在 DataGrip 里面用不了 check 约束,…

Android设计模式详解之桥接模式

前言 桥接模式也称为桥梁模式,是结构型设计模式之一; 定义:将抽象部分与实现部分分离,使它们都可以独立地进行变化; 使用场景: 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性&a…

laravel5.5.50之权限(laravel-permissions)的使用

目录 一、安装laravel-permissions扩展 二、生成数据库迁移文件 三、执行数据迁移 四、php artisan migrate 生成的表 五、生成配置信息 六、在user模型下加载角色相关信息 七、测试角色、权限 一、安装laravel-permissions扩展 #不指定安装版本的命令 composer requir…

提升舆情监测软件技术方法解读,舆情监测技术逻辑?

舆情监测是指通过技术手段,对网络舆情进行监测、分析、挖掘和可视化展示的工作。舆情监测可以帮助企业、政府、组织等了解社会舆论,掌握社会动态,协助决策,提升社会影响力。TOOM舆情监测系统介绍提升舆情监测软件技术方法解读&…

Zabbix与乐维监控对比分析(七)——网络功能篇

前面我们详细介绍了Zabbix与乐维监控在架构与性能、Agent管理、自动发现、权限管理、对象管理、告警管理、可视化及图形图表方面的对比分析,接下来我们将对二者网络功能方面进行对比分析。 本篇是Zabbix与乐维监控对比专题系列文章的第七篇——网络功能篇&#xff…

gitlab+jenkins+docker持续集成环境搭建实战

介绍 什么是持续集成? 持续集成(CI)是在源代码变更后自动检测、拉取、构建和(在大多数情况下)进行单元测试的过程。持续集成是启动管道的环节(尽管某些预验证 —— 通常称为 上线前检查(pre-flight checks)…

2023 数据结构复习

函数编程题 7-1 交换二叉树中每个结点的左孩子和右孩子 #include<stdio.h> #include<stdlib.h> typedef struct Node {char data;struct Node *left, *right; } Node, *Tree;Tree create() {char ch;Tree tree NULL;if (scanf("%c", &ch)) {//当有…

P5015 [NOIP2018 普及组] 标题统计————C++

题目 [NOIP2018 普及组] 标题统计 题目描述 凯凯刚写了一篇美妙的作文&#xff0c;请问这篇作文的标题中有多少个字符&#xff1f; 注意&#xff1a;标题中可能包含大、小写英文字母、数字字符、空格和换行符。统计标题字 符数时&#xff0c;空格和换行符不计算在内。 输入…

JAVA开发与架构(携程架构实践)

发这篇博文主要是想学习一下携程的架构。携程出了一本《携程架构实践》&#xff0c;无奈现在还没开源。看京东价大概109元人民币。如果看到次博文的网友能发我一本《携程架构实践》pdf。不胜感谢。或者实体书籍也行。其实我不怎么需要实体书籍&#xff0c;因为技术的东西很快会…