synchronized真的很重么?

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

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

本文就来探讨一下,jdk对synchronized 优化,包括锁升级、锁粗化、锁消除。

一、锁升级

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

偏向锁

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

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

轻量级锁

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

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

当出现这种情况的时候,偏向锁就会升级为轻量级锁。轻量级锁就是指虽然可能会出现多线程来加锁的情况,但是并不存在锁竞争的情况,并不会存在锁冲突。

自旋锁

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

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

重量级锁

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

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

二、锁消除

先来看一段代

java;gutter:true; public class SynchronizedDemo {</p> <pre><code>public static void main(String[] args) { Object monitor = new Object(); synchronized (monitor){ System.out.println("加锁成功...."); } } </code></pre> <p>}</p> <pre><code> 通过上面代码我们可以看出,synchronized 的锁对象其实是方法的一个局部变量,那么对于这种情况,不会出现多线程竞争同一把Object 对象锁的情况,那么在运行的时候就会忽略这个synchronized 加锁的过程。 说的官方一点就是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。 ## 三、锁粗化 ;gutter:true;
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真的很重么?

Original: https://www.cnblogs.com/zzyang/p/16336060.html
Author: 三友的java日记
Title: synchronized真的很重么?

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/621752/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • idea内置tomcat中java代码热更新

    按照上图设置后,然后修改代码后按shift+F9快捷键,即可实现代码更新,这时在debug模式下会看到代码变更后的输出 Original: https://www.cnblogs….

    Java 2023年5月29日
    083
  • Java:JVM基础——非堆部分(Java8)

    PS:现在普遍都是 Java8 和 Java11 了,我不打算写 Java8 之前的了,本文出现的虚拟机都是 HotSpot 虚拟机 1、 JVM 内存结构图 2、 Java Vi…

    Java 2023年6月7日
    056
  • Spring自动装配

    前提 1、byName 2、byType 3、使用注解进行自动装配 (1)spring配置文件设置约束 (2)开启属性注解支持 (3)@Autowired (4)@Qualifie…

    Java 2023年6月5日
    078
  • session和cookie的区别

    一·概念理解 首先呢,要了解session和cookie的区别先要了解以下几个概念: 1、 无状态的HTTP协议: 协议,是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的…

    Java 2023年5月30日
    079
  • 整理最近用的Mongo查询语句

    最近做了几个规则逻辑。用到mongo查询比较多,就是查询交易信息跑既定规则筛选出交易商户,使用聚合管道进行统计和取出简单处理后的数据,用SQL代替业务代码逻辑的判断。 MongoD…

    Java 2023年6月7日
    075
  • 设计模式 23 访问者模式

    访问者模式(Visitor Pattern)属于 行为型模式 生活中经常会有这样的情况,同样的事物不同人有完全不同的感受,正所谓 一千个读者一千个哈姆雷特。 程序中也是一样,往往不…

    Java 2023年6月6日
    0106
  • redis简述

    redis是什么? Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、…

    Java 2023年6月14日
    075
  • Java之线程池深度剖析

    1.线程池的引入引入的好处:1)提升性能。创建和消耗对象费时费CPU资源2)防止内存过度消耗。控制活动线程的数量,防止并发线程过多。使用条件:假设在一台服务器完成一项任务的时间为T…

    Java 2023年5月29日
    078
  • nginx 屏蔽ip

    在网站运行过程中,我们有的时候需要对某个IP或者IP段进行封禁,禁止IP访问本服务器,如果服务器的环境用的是Nginx,下面我们来看看Nginx如何禁止某个IP访问! 方法一:首先…

    Java 2023年5月30日
    082
  • Java三大特性之多态

    多态概述 Java有三大特性:封装、继承和多态。 ​ 那么什么是多态呢?所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序…

    Java 2023年6月5日
    089
  • SpringCloud系列之客户端负载均衡Netflix Ribbon

    1. 什么是负载均衡? 负载均衡是一种基础的网络服务,它的核心原理是按照指定的负载均衡算法,将请求分配到后端服务集群上,从而为系统提供并行处理和高可用的能力。提到负载均衡,你可能想…

    Java 2023年5月30日
    093
  • 一文学会Java的交互式编程环境jshell

    什么是交互式编程环境?重点词 交互,在这样的编程环境中,你每输入一行代码,环境都会给你一个反馈,这就是交互式的编程环境。这种编程环境并不太适合工程化的复杂性需求,但在一些快速验证、…

    Java 2023年6月9日
    099
  • java.rmi.ConnectException: Connection refused to host: 127.0.0.1

    搭建 otter 的manger节点,然后用浏览器访问manager时,报错: java.rmi.ConnectException: Connection refused to h…

    Java 2023年5月29日
    0112
  • window server 2019环境下将nginx配置为开机自启动服务

    公司window服务器上面有个nginx在跑,重启服务器后没有自动启动,需要手动运行nginx,如果是非正常重启业务可能就中断了 1、下载WinSW(window service …

    Java 2023年5月30日
    067
  • 用栈模拟计算器以及中缀转后缀表达式(逆波兰表达式)

    后缀表达式(逆波兰表达式)运算方法 从左向右读取表达式 遇到数字就压入栈中 遇到运算符就弹出栈顶和次顶元素。用 次顶元素 运算符 栈顶元素,并将运算结果压入栈中,直到栈为空,最终结…

    Java 2023年6月16日
    092
  • 变量、常量、作用域(Java)

    基本介绍 1.变量 定义:可以变化的量 2.变量声明 Java是一种强制类型语言,每一个变量必须声明类型 3.变量名,变量类型和作用域 Java变量是程序中最基本的存储单元,其要素…

    Java 2023年6月9日
    093
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球