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

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis