Java小案例-Synchronized真的很重量级吗?

2023-12-29 17:20

本文主要是介绍Java小案例-Synchronized真的很重量级吗?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

synchronized 是java中常见的保证多线程访问共享资源时的安全的一个关键字。很多人在讲到synchronized 时都说synchronized 是一把重量级的锁,那么synchronized 真的很重么?

synchronized 在jdk 1.6以前(不包括1.6)的确是一把很重的锁,每次使用锁的时候都是直接向操作系统请求的,所以效率低,且占资源,但是在jdk1.6以后,jvm对synchronized 进行了优化,加入了锁升级的功能,使得synchronized 在一些情况下并不是一把重量级的锁,而是一个很轻的一把锁。

一、锁升级

锁升级其实是指,随着多线程并发加锁的程度提高而相应的对锁的状态的升级,可以分为:偏向锁、轻量级锁、自旋锁、重量级锁;

偏向锁

偏向锁不是一把锁,而是代表了当前synchronized 锁状态。偏向锁是一把很轻的锁,当只有一个来线程加锁的时候,此时synchronized 锁就会变成偏向锁,偏向锁代表这个锁偏向这个线程,就是说当这个线程再次来加锁的时候,不需要再向操作系统申请资源,而是很快就能获取到锁,减少了申请锁的开销。

为什么需要偏向锁。因为在大多数情况下,多线程竞争同一把锁的情况是很少的,那么在没有多线程并发竞争的情况下,其实没必要再去向系统申请重量级锁了,我就用目前这把偏向锁就够了,因为申请重量级会耗费比较大的资源。

轻量级锁

当随着更多线程来加锁的时候,偏向锁就会无法满足使用的条件了,因为偏向锁认为加锁的线程只有一个。

那么多线程加锁有可能会出现这种情况。当会有两个及以上的线程来加锁,但是没有出现同时来竞争锁的情况,也就是说虽然有多个线程来加锁,可能会出现A线程加完锁之后释放了锁,此时B来加锁,发现并没有线程持有锁,也就说没有线程跟B来竞争,也就相当于多线程来交替加锁的情况。当出现这种情况的时候,偏向锁就会升级为轻量级锁。轻量级锁就是指虽然可能会出现多线程来加锁的情况,但是并不存在锁竞争的情况,并不会存在锁冲突。

自旋锁

上面说到随着加锁的线程变多,出现了多线程交替加锁的情况,偏向锁会升级为轻量级锁,但是随着并发加锁的线程越来越多,出现了多个线程同时来加锁的情况,也就不是交替加锁,那么此时轻量级锁已经不适合使用了,但是jvm为了防止锁直接升级为重量级锁(因为挂起线程和恢复线程的操作都需要转入到内核态中完成,这些操作给系统的并发性能带来很大的压力),加入了线程自旋的机制。所谓的自旋就是虽然加锁失败了,但是有可能出现其他线程很快就释放锁的情况,那么就尝试不断的自旋来尝试加锁,而不是直接将锁升级为重量级锁。

自旋锁的好处就是用来减少操作系统的压力。

重量级锁

但是随着加锁的线程不断增多,自旋了一定的时间或者次数也没有成功加锁,那么锁就会升级为重量级锁,因为自旋会消耗cpu,一直这么自旋也不是很好的选择,所以就会升级为重量级锁。升级为重量级锁之后,所有来加锁的线程加锁失败之后,就会加入等待的队列中,等待别的线程释放锁之后再进行锁的竞争。

通过以上的分析,我们也看出了,synchronized 在最开始的时候并不是上来就是一把重量级的锁,而是随着多线程并发竞争锁的激烈程度的提高来不断的升级,慢慢变成重量级锁,在整个升级的过程会经历偏向锁、轻量级锁、自旋锁、重量级锁的过程。

二、锁消除

先来看一段代码

public class SynchronizedDemo {
    public static void main(String[] args) {        Object monitor = new Object();        synchronized (monitor){            System.out.println("加锁成功....");        }    }}

通过上面代码我们可以看出,synchronized 的锁对象其实是方法的一个局部变量,那么对于这种情况,不会出现多线程竞争同一把Object 对象锁的情况,那么在运行的时候就会忽略这个synchronized 加锁的过程。

说的官方一点就是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

三、锁粗化

public class SynchronizedDemo {
    private static final Object MONITOR = new Object();
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {            synchronized (MONITOR) {                System.out.println("加锁成功....");            }        }    }}

通过这段代码的分析,可以看出,循环内部每次都对同一个对象进行加锁和解锁,对于这种一串零碎的操作都对同一个对象加锁情况,虚拟机将会把加锁同步的范围扩展 (粗化)到整个操作序列的外部。以上述代码为例,也就是扩展到把for循环这个操作加锁,这样只需要加锁一次就可以了。

总结

所以,通过本篇文章可以看出,jdk对synchronized 其实进行了一系列的优化来尽可能减少加锁时对于性能的消耗,包括锁升级、锁消除、锁粗化。希望通过本篇文章可以让你对synchronized 底层又有一个全新的认识。

联系方式

关于文章中大家有任何疑问可以通过关注公众号《编程乐学》进行留言,同时,公众号还有更多有趣的项目以及关于学习编程的笔记资料大家可以看看,欢迎大家进行留言。

这篇关于Java小案例-Synchronized真的很重量级吗?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/550143

相关文章

springboot集成easypoi导出word换行处理过程

《springboot集成easypoi导出word换行处理过程》SpringBoot集成Easypoi导出Word时,换行符n失效显示为空格,解决方法包括生成段落或替换模板中n为回车,同时需确... 目录项目场景问题描述解决方案第一种:生成段落的方式第二种:替换模板的情况,换行符替换成回车总结项目场景s

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。