【JAVA】#详细介绍!!! synchronized 加锁 详解(2)

news2025/6/11 8:07:56

本篇主要是针对 synchronized锁的优化过程来介绍,针对synchronized的加锁优化过程来了解上篇所提到的synchronized的锁特性。

目录

1. synchronized锁的特性

2.synchronized 锁的升级过程

2.1 总过程:

2.2 偏向锁

2.3 轻量级锁

2.3.1自旋锁vs自适应自旋锁

2.4 重量级锁

2.5 锁的其他优化

2.5.1 锁消除

2.5.2 锁粗化

2.6 图表总结:



 


1. synchronized锁的特性

1. 既是乐观锁,也是悲观锁

2.即使轻量级锁,也是重量级锁

3. 是非公平锁

4. 是可重入锁

5. 不是读写锁

提示:了解这些锁策略的可以移步:http://t.csdn.cn/HmbsY

2.synchronized 锁的升级过程

2.1 总过程:

锁对象状态优化过程:

无锁->偏向锁->轻量级锁->重量级锁

锁的状态一共分为四组:无锁状态,偏向锁,轻量级锁,重量级锁。

1.当还没有线程进行加锁时,当前对象锁处于无锁状态,

2.当有一个线程进行加锁操作时该锁会升级至偏向锁状态,

3.如果此时再来一个线程进行加锁操作,此时就锁冲突了,那么锁立即升级为轻量级锁,针对前面偏向的线程立刻进行加锁,避免线程不安全;

4.如果此时锁冲突较大,且线程每次加锁的时间较长,那么锁就会升级为重量级锁,直接依赖于操作系统底层的metux(互斥锁),使得同一时间只有一个线程在执行加锁代码,其他没抢到锁的线程会挂起等待(堵塞)

注意:锁升级是不可逆的,升级了之后策锁略不会向前退化

synchronized 加锁的线程对象是存储在锁对象的对象头中的,其中对象头中有一个对象标记(Mark Word)来标记当前锁执行的是哪种锁策略(例如偏向锁,轻量级锁等),并且记录了持有锁的线程对象。当然Mark Word中还有其他的标记状态,大致如下图所示

2.2 偏向锁

偏向锁主要是针对没有多线程竞争的场景做出的优化,当前锁总是只有同一个线程在获取锁,并不会有线程安全问题,所以也就不需要加锁去浪费CUP资源

当锁只有一个线程来加锁时,次数Mark Word 的偏向锁标记就会标记当前加锁线程,该锁也顺势升级为偏向锁,在此之后这个线程再来尝试获取该锁则不需要进行任何操作就可直接获取到锁,这样也就省去了中间申请锁操作的各种流程,进而提升了效率

2.3 轻量级锁

轻量级锁主要针对线程竞争不激烈时,避免锁使用重量级锁造成资源的情况下做出的优化

当存在锁竞争时,synchronized锁将会从偏向锁升级为轻量级锁保证线程安全

轻量级锁顾名思义针对重量级锁它是更轻量的,轻量级锁的锁竞争并不会直接去申请使用底层的mutex锁,而是把锁对象的Mark word信息交给到线程让线程去执行CAS操作,此时竞争失败的线程则进行基于CAS的自旋(自旋锁策略)

2.3.1自旋锁vs自适应自旋锁

轻量级锁前期是的锁策略是CAS自旋转来解决,但是旋转多少次是一个直接深究的问题,当线程占用锁的时间很长时,此时多线程一直在进行无实际意义自旋无疑是大大的消耗了CPU的资源,此时还不如直接升级为重量级锁

那么刚开始的JDK1.6版本轻量级锁的自旋策略每次是旋转固定的次数,如果自旋最大次数之后还没执行,那么锁便会升级为重量级锁

但是在JDK1.7时优化自旋的次数优化为自适应次数,意思就是自旋的次数会根据前面线程旋转的次数而进行自适应调整,使得锁自旋的次数不再固定,怎么做的原因就是,避免极端情况下有线程自旋次数过多导致并不激烈的竞争状态直接升级为重量级锁

总而言之还是那句话:真正意义上的线程堵塞等待永远是下策,因为线程阻塞以为着多线程并发变成了串行,效率肯定会大受影响。属于是牺牲效率保证安全。

2.4 重量级锁

当轻量级锁自旋等待时间过长锁竞争过大,此时就不能一直自旋去一直无效的占用着cpu的内核资源了,那么锁会膨胀为重量级锁,每次加锁都会真正的去调用操作系统的mutex(互斥锁)那么竞争失败的线程会挂起等待(放·弃CPU资源堵塞等待),直到加锁的线程解锁后再去重新尝试加锁。

优点是确保了线程安全

缺点:每次加锁都是从用户态去内核态申请加锁,加锁失败则堵塞等待,加锁成功则从内核态返回到用户态,我们都知道用户态和内核态的频繁切换对计算机来说已经是沧海桑田了

那么一套操作下来,如果加锁线程解锁了,然后等到操作系统去唤醒加锁的线程去重新尝试加锁效率就很低下

2.5 锁的其他优化

2.5.1 锁消除

锁消除是编译器和JVM底层的一种优化机制

通过编译编译器会提前衡量一下当前锁是否会存在竞争,如果加锁解锁在单线程执行下,此时是没必要的,所以编译器和JVM会判定当前锁可以消除

此时就会不执行加锁操作就执行加锁代码,减少了一定的开销

例子:

StringBuffer str = new StringBuffer();//线程安全的字符串对象
sb.append("a");
sb.append("b");
sb.append("c");
sb.append("d");

上诉代码中定义了一个StringBuffer对象str,而StringBuffer是一个可修改的,并且内部的关键方法使用了synchronized加锁保证了线程安全,此时我们在单个线程中对str进行修改,此时编译器和JVM就会判断当前操作并不存在线程安全,属于无效加锁,那么就会把加锁的过程给优化了,那么下面的那些append方法,虽然它内部是使用了synchronized方法,但本质并没有进行加锁操作。这些过程就是编译器和JVM进行的锁消除优化

2.5.2 锁粗化

锁粗化也是编译器和JVM做出的一种优化。

当一段代码中,在较短时间内前后多次执行对同一锁对象进行加锁操作,频繁加锁导致锁的粒度较细,那么编译器和JVM就会把当前的多个锁进行整合,让这些操作在一次加锁过程中执行完,使得锁的粒度边粗

例子:

public class Demo2 {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        int num1 = 0;
        int num2 = 0;

        synchronized (lock){
            num1++;
        }
        synchronized (lock){
            num2++;
        }

    }
}

中间的加锁优化成:

public class Demo2 {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        int num1 = 0;
        int num2 = 0;

        synchronized (lock){
            num1++;
            num2++;

        }
        System.out.println(num1+num2);
    }
}

经过编译器加JVM的判定,锁的粒度过细,那么此时就会进行优化,整合锁,避免频繁的加减锁,使得资源浪费,进而提高效率

2.6 图表总结:


本章的内容介绍到这里就差不多结束了,希望各位看官老爷都能有所收获;

有写错或者不足的地方也欢迎指正

 

 

 

 

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

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

相关文章

网络安全之认识勒索病毒

一、什么是勒索病毒 勒索病毒,是一种新型电脑病毒,伴随数字货币兴起,主要以邮件、程序木马、网页挂马、服务器入侵、捆绑软件等多种形式进行传播,一旦感染将给用户带来无法估量的损失。如果遭受勒索病毒攻击,将会使绝…

Flink任务提交流程

抽象流程 抽象级别:不管是什么模式,大体上就是上面这个流程。 任务提交给分发器分发器把任务提交给JobManager上的JobMaster组件JobMaster收到任务之后,就会想JobManager上的ResourceManager去请求SlotJobManager上的ResourceManager会提供给…

3.1.2栈的顺序存储实现

(1)初始化一个顺序栈/栈的判空操作 与顺序表的声明类似 就是要加上一个栈顶指针top 然后把别名SqList改为SqStack 我们发现top指针的大小就是数组下标。 当空栈时,top指针为-1. (2)进栈操作 ep:插入一…

版本控制:git的基本使用

1.git基本介绍及安装 学习网址:Git - Book 安装步骤: Git - 安装 Git 安装完可以在本地电脑上查看: cmd为windows环境 bash为linux的环境 2. Git常用命令 牛客网项目——前置技术(五):版本控制_平什么阿的博客-C…

ffmpeg关于视频前几秒黑屏的问题解决

关于音频播放器视频前两秒黑屏的解决,及QtAV和ffmpeg的环境搭建(软件包可以找李青璠提供,也可以自己下)首先我们可以参考下面两个博客进行ffmpeg的搭建,第一个博客的问题可以在第二个博客里寻求方法解决。其中第一个博…

服务器上后台运行python程序

Linux中将代码nohup后台执行、查看正在运行代码、结束进程写在最前面环境代码示例nohup指令& 后台运行2>&1 错误内容重定向到标准输出查看当前python相关进程结束进程nohup后台pip下载安装写在最前面 一直是pycharm运行服务器上代码,但存在问题&#xf…

3.1、线程概述

3.1、线程概述1.线程概述2.线程和进程区别3.线程和进程虚拟地址空间4.线程之间共享和非共享资源①共享资源②非共享资源5.线程版本NPTL1.线程概述 与进程(process)类似,线程(thread)是允许应用程序并发执行多个任务的…

通达信指标没有了怎么找回

通达信指标没有了可以恢复,不用太慌张,通达信会自动备份指标公式,可以通过备份文件找回。 1、找到通达信安装文件夹,一般是new_tdx,但是版本不同,安装文件夹可能有区别。本文以new_tdx这个文件夹为例。 如…

什么是零代码与低代码?有什么区别与联系?未来趋势

目前传统软件开发模式并不能很好地满足企业的需求:高人力成本、长研发时间、运维复杂,需求变化快,技术更新快,人员流失。这时零代码或低代码工具出现在市面上并被关注就是必然趋势了。对于不太了解两者的人来说,零代码…

【mysql性能调优 • 三】字符集和校验规则

前言 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系…

Linux下Nginx配置SSL模块,Nginx安装SSL,Nginx支持https配置详细教程

前提:Linux安装Nginx,参考教程:CentOS7安装Nginx完整教程,Linux系统下保姆式安装Nginx教程 | 老麻 安装好Nginx之后,需要支持SSL时,要单独安装SSL模块,方法如下: 输入 ./nginx –V 命…

2345看图王阻止文件删除和U盘弹出 - 解决方案

2345看图王阻止文件删除和U盘弹出 - 解决方案前言2345看图王解决方案临时方案永久方案前言 用户在使用2345看图王查看图片后,可能会出现图片文件/文件夹无法删除或U盘无法弹出等问题,这是因为2345看图王的辅助模块正在占用图片文件,因此无法…

设计分布式日志系统

一、日志 1.1、什么是日志 日志是一种按照时间顺序存储记录的数据,它记录了什么时间发生了什么事情,提供精确的系统记录,根据日志信息可以定位到错误详情和根源。按照APM概念的定义,日志的特点是描述一些离散的(不连…

Spark----DataFrame和DataSet

Spark之DataFrame和DataSet 文章目录Spark之DataFrame和DataSetDataFrameDSL 语法创建DataFrame查看DataFrame的Schema信息只查看列数据的6种方式按照“age”分区,查看数据条数增加列withColumn修改列名withColumnRenamedRDD 转换为 DataFrameDataFrame 转换为 RDD转…

如何使用双轴XY平台绘制斜向多边形

1. 功能说明 本文示例将实现双轴XY平台绘制斜向多边形的功能。 2. 直角坐标机器人的结构设计 直角坐标机器人各个运动轴通常对应直角坐标系中的X轴、Y轴和Z 轴,其中X 轴和Y 轴是水平面内运动轴,Z轴是上下运动轴。在绝大多数情况下直角坐标机器人的各个直…

SpringBoot集成Easy-Es

文章目录SpringBoot集成Easy-Es一、集成demo二、索引CRUD创建索引查询索引更新索引删除索引三、数据CURD四、参数文档SpringBoot集成Easy-Es Easy-Es(简称EE)是一款基于ElasticSearch(简称Es)官方提供的RestHighLevelClient打造的ORM开发框架&#xff0c…

C语言—实用调试技巧

实用调试技巧什么是bug?调试是什么?有多重要?什么是调试调试的基本步骤Debug和Release的介绍Windows环境调试介绍调试环境的准备快捷键的使用调试的时候查看程序当前信息查看断点信息查看临时变量的值查看局部变量的值查看内存信息查看汇编信…

PERSIANN 降雨数据使用教程

一、前言PERSIANN,“使用人工神经网络从遥感信息中估算降水”,是一种基于卫星的降水检索算法,可提供近乎实时的降雨信息。该算法使用来自全球地球同步卫星的红外 (IR) 卫星数据作为降水信息的主要来源。 红外图像的降水基于云顶温度和降水率之…

ESP32驱动1.28寸GC9A01播放视频(一、视频分辨率的调整和视频格式的转换)

ESP32驱动1.28寸GC9A01播放视频(一、视频分辨率的调整和视频格式的转换)播放前准备转换视频分辨率用FFmpeg将.MP4转换为.mjpeg格式FFmpeg的win10环境搭建FFmpeg的下载环境变量的搭建MP4转换成mjpeg格式总结播放前准备 1.28寸GC9A01屏幕的分辨率是240x24…

prometheus标签

004 标签 1. 作用 Prometheus中存储的数据为时间序列,是由Metric的名字和一系列的标签(键值对)唯一标识的, 不同的标签代表不同的时间序列,即 通过指定标签查询指定数据 。 指标标签实现了查询条件的作用,可以指定不同的标签过滤不同的数据…