1、最初的 synchronized

它默认对临界资源添加重量级锁,即使可能并不存在竞争,只要走到临界区通通给你加锁。

现在来回答问题:

1) 如果是低于 JDK 1.5,抱歉你没得选,只能先将就着用 synchronized 重量级锁

2)如果你的 JDK 版本是1.5 那么推荐 ReentrantLock,这个时候的 synchronized 还是默认加重量级锁 。

接下来我们看看 JDK 1.6 干了啥

2、synchronized 的优化

在JDK 1.6 ,官方引入了如下的一系列优化:

  1. 锁消除
  2. 锁粗化

    3)锁升级:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁

    重点介绍影响比较大的 "锁升级"

    无锁: 不存在任何互斥资源的竞争,无需加任何锁。

    偏向锁:当临界资源被某一个线程访问到了,那么在临界资源的对象头里标记偏向锁;在添加偏向锁的线程结束锁定前:1) 如果没有其它线程竞争该临界资源,效率等同无锁;2) 若此时另一个线程也希望访问该临界资源,那么偏向锁将升级为轻量级锁。(补充,新版本的JDK中,偏向锁已经被移除)

    轻量级锁:何谓轻量级锁? 就是当被多个线程竞争的临界资源上,若存在轻量级锁时,等待获取锁的线程不会直接进入等待队列,而是先尝试指定次数的自旋,如果超过默认自旋次数仍未获取到锁,那么轻量级锁将会升级为重量级锁。(自旋次数JVM可配置,但是一般不建议动它)

    重量级锁:如果临界资源上已经被某个线程,添加了重量级锁,那么当其它线程过来竞争锁的时候,就会直接进入等待队列,直至当前线程推出临界区,才会被唤醒、执行临界区。

然后我们重点说一下,轻量级锁的自旋,它好在哪里?了解JVM 并发编程模型的人都知道,在 JDK 21 LTS 推出虚拟线程之前,线程的调度是依托于底层的操作系统内核来完成的,相当于线程的:创建、等待、唤醒、销毁 ... 等等动作都直接依赖操作系统内核的API来完成,这是非常重型的操作, 所谓重量级锁,就是因为它直接阻塞别的线程,是一个非常耗费资源的操作。

而引入这个特性之后,其实 synchronized 的性能已经有了长足的进步,到了这里,其实跟 ReentrantLock 对比已经丝毫不怵了;甚至因为升级这个特性,有些时候 synchronized 会有更强悍的表现。


当 JDK1.6 <= version < JDK 21, 我们来回答问题:

高并发下 ReentrantLock 和 synchronized 哪个性能更好?

这个题可能没有标准答案,这里需要结合存在临界资源竞争的场景,来具体分析实际的临界操作行为,ReentrantLock 和 synchronized 谁更好。

  1. 当操作单一且耗时较短时,可以使用 synchronized 来做同步控制,因为只要不升级为重量级锁,性能损耗是极小的;且由于官方基于JVM做了针对性的优化, 那么它的适配性应当是更好的,如果没有复杂需求、临界操作并不复杂的时候,建议使用synchronized,因为它足够简单,性能也不在是鸿沟。
  2. 而 ReentrantLock它功能更加强大,它是在用户态下基于 AQS (Abstract Queued Synchornizer)实现,它不会阻塞内核线程。ReentrantLock 支持中断等待、支持公平锁、支持锁定多个对象,这些都是 synchronized 所不能具备的特性,在较为复杂的场景下,尤其建议使用 ReentrantLock 。

这里有一个值得思考的事情, AQS 实现的Lock 要考虑一个点,如果临界区很耗时,存在多个线程排队的时候,由于它没有进行上下文的切换,说明排队中的线程是没有被挂起的。在非虚拟线程场景下,jvm线程跟操作系统内核线程是一比一深度绑定的,那么是否应该指出这种情景下可能存在,cpu空转浪费资源。

不论是从ReentrantLock,还是synchronized的角度看,我们做程序设计时,应当保证临界区尽可能的小,尽可能的高效。

这里判断的临界值是:synchronized 阻塞再拉起线程的消耗,是否远大于临界区的执行时长。如果是,那么必须使用 Reentrantlock。否则,当阻塞再拉起线程的代价,远小于临界区执行的消耗时,应当慎重考虑使用何种锁。起码该JVM线程被阻塞后, 内核线程能释放出来干点别的事。

-- 如果有人,不看版本,不分析业务临界操作行为,就直接说谁好谁差,一律视为半桶水就行了。

3、但是,JAVA的最终答案 JDK 21 LTS 来了

JDK8 之后,最值得升级的 JDK 版本,虚拟线程,号称史诗级的加强,下一个主权就是它了!!!

自此,虚拟线程成为了JVM正式支持的功能.(jdk 19 的虚拟线程只是预览功能)

这里就不得不重点介绍一个特性了:JVM 并发编程模型,前文提到,在JDK 21 LTS 推出虚拟线程之前,jvm 的线程调度依赖操作系统内核。

在 JDK 21 的时代,JVM的线程被划分为了如下两种类型:

平台线程:所谓平台线程就是,跟操作系统内核深度绑定基于操作系统内核实现,其实它就是 JDK 21 LTS 版本之前的java 线程,"平台线程" 的调度依赖操作系统内核。

虚拟线程:JDK 21 LTS 新特性,"虚拟线程" 只在 JVM 内部调度,"虚拟线程"的调度,不再依赖操作系统内核的线程调度,它只发生在 JVM用户态,所以 "虚拟线程" 的调度行为是非常轻量的。"虚拟线程" 有等待队列,"虚拟线程"的执行需要靠"平台线程"来调度等待队列。可以理解为多个"虚拟线程",在竞争"平台线程" 的cpu时间分片.

在虚拟线程的场景下,我们开发者,调用的是虚拟线程,"平台线程"由 JVM自身进行控制。我们在 "虚拟线程" 内部不论执行了多少休眠、阻塞等操作,丝毫不会影响"平台线程"的调度。

  1. 当虚拟线程休眠、阻塞、等待时,那么它将会从 "平台线程" 上 unmount 并进入等待队列
  2. 当 "平台线程" 上unmount 了某个虚拟线程,或者某个虚拟线程执行结束了,那么 "平台线程" 会在依据一定的规则,从虚拟线程等待队列里唤起并mount某一个虚拟线程,执行时间分片。
  3. 虚拟线程所需的空间极小,在 JDK 21 环境下,可以清理拉起数万、甚至数十万的虚拟线程,同等硬件资源下,相较于之前的并发编程模型,可以更多拉起的线程数,已经到指数级了。这是几近可以跟 golang 在高并发领域,掰头一下的水平了,这够高并发了吧?

    由此来看 "虚拟线程" 这种用户态实现的并发编程模型,在高并发场景下,要远胜于之前的 "操作系统内核态" 并发编程模型。

我用了这么多文字介绍JVM并发编程模型,那么它跟 ReentrantLock 和 synchronized 有什么关系呢?

这是因为,synchronized 会将虚拟线程固定在平台线程上,在 synchronized 临界区执行结束之前,无法被 unmount, 这样是会影响高并发场景下虚拟线程的调度效率的。

而ReentrantLock 就不会有这样的问题了,碰到阻塞操作时,"虚拟线程"会很丝滑的从"平台线程"上unmount,让出"平台线程的"时间分片。


现在我们基于 JDK 21 LTS 版本来回答问题:高并发下 ReentrantLock 和 synchronized 哪个性能更好?

1)首先为了有更好的高并发体验(IO密集型),应当使用 "虚拟线程"进行开发

2) 由于 synchronized 会阻止虚拟线程遇到阻塞后从 "平台线程"上 卸载,所以不推荐在高并发场景使用它,它会极大的影响虚拟线程在平台线程上的调度。在虚拟线程的版本, "ReentrantLock" 才是最终答案。

当然你要是说我不用 "虚拟线程" ,那答案同第二节所述。

高并发时为什么推荐ReentrantLock而不是synchronized的更多相关文章

  1. 关于sphinx+PHP在高并发时响应性能低下的解决办法

    经过多次压力测试,发现sphinx在高并发时出现负载突然提升,并且响应速度明显下降.经过多方面的排查,发现是由于PHP与sphinx自带的 searchd进行socket的连接之后,系统内存有大量的T ...

  2. [转]你如何面对—LNMP高并发时502

    From : http://www.topthink.com/topic/5683.html 之前php-fpm配置: 单个php-fpm实例,使用socket方式,内存8G 静态方式,启动php-f ...

  3. j2ee高并发时使用全局变量需要注意的问题

    原文:https://blog.csdn.net/jston_learn/article/details/21617311 开发中,全局变量的使用很频繁,但对于多线程的访问,使用全局变量需要注意的地方 ...

  4. php session在高并发时可能存在的问题。

    如果同一个客户端并发发送多个请求,而每个请求都使用了Session,那么PHP Session锁的存在会导致服务器串行响应这些请求,而不是并行.这是因为在默认情况下,PHP使用文件存储Session数 ...

  5. 【多线程与高并发原理篇:4_深入理解synchronized】

    1. 前言 越是简单的东西,在深入了解后发现越复杂.想起了曾在初中阶段,语文老师给我们解说<论语>的道理,顺便给我们提了一句,说老子的无为思想比较消极,学生时代不要太关注.现在有了一定的生 ...

  6. 并发与高并发(八)-线程安全性-原子性-synchronized

    前言 闲暇时刻,谈一下曾经在多线程教程中接触的同步锁synchronized,相当于复习一遍吧. 主要介绍 synchronized:依赖JVM Lock:依赖特殊的CPU指令,代码实现,Reetra ...

  7. 《JAVA高并发编程详解》-volatile和synchronized

  8. java高并发_博客-网址-资料 推荐

    大概说一下自己作为入门学习java高并发的博客地址,很不错在自己的博客里记录一下:如果能有刷到我的博客的骚年,又刚好想了解java高并发,强烈推荐看看 地址:http://www.itsoku.com ...

  9. Redis 高并发带来的一些问题

    前言 本文讲述Redis在遇到高并发时的一些问题.即遇到大量请求时需要思考的点,如缓存穿透 缓存击穿 缓存雪崩 热key处理.一般中小型传统软件企业,很难碰到这个问题.如果有大并发的项目,流量有几百万 ...

  10. Redis高并发分布式锁详解

    为什么需要分布式锁 1.为了解决Java共享内存模型带来的线程安全问题,我们可以通过加锁来保证资源访问的单一,如JVM内置锁synchronized,类级别的锁ReentrantLock. 2.但是随 ...

随机推荐

  1. [转帖]Linux命令之——rsync

    文章目录 1 rsync是干什么用的 2 rsync和scp有什么区别 3 rsync简单用法介绍 rsync四种工作方式 1. 本地文件系统上实现同步 2. 本地主机使用远程shell和远程主机通信 ...

  2. [转帖]十九、Linux性能优化实战学习笔记- 为什么系统的Swap变高了?

    目录 一.什么是文件页?什么是脏页?什么是匿名页? 二.linux swap原理 换出 换入 三.内存回收的时机 1.直接内存回收 2.kswapd0内核线程 四.NUMA 与 Swap关系 五.sw ...

  3. [转帖]内存管理参数zone_reclaim_mode分析

    zone_reclaim_mode 官方解释 调整方法 调整的影响 官方解释 最近在性能优化,看到了zone_reclaim_mode参数,记录备用 zone_reclaim_mode: Zone_r ...

  4. [转帖]Jmeter学习笔记(十一)——定时器

    https://www.cnblogs.com/pachongshangdexuebi/p/11571524.html 默认情况下,Jmeter线程在发送请求之间没有间歇.不设置定时器,短时间内会产生 ...

  5. [转帖]Redis命令详解:Keys

    https://jackeyzhe.github.io/2018/09/22/Redis%E5%91%BD%E4%BB%A4%E8%AF%A6%E8%A7%A3%EF%BC%9AKeys/ 介绍完Re ...

  6. HTTPS下tomcat与nginx的前端性能比较

    HTTPS下tomcat与nginx的前端性能比较 摘要 之前比较http的web服务器的性能. 发现nginx 比 tomcat 要好 50% 然后想到, https的情况下不知道两者有什么区别 所 ...

  7. [转贴]30 分钟学会 AWK

    30 分钟学会 AWK https://mp.weixin.qq.com/s/X0ire4dYiceC2CzPU6JsSw? Linux爱好者 2017-01-08   (点击上方公众号,可快速关注) ...

  8. TypeScript中Never类型和类型断言

    Never 类型 never类型表示:那些永不存在的值的类型. 例如:never类型是那些总是会[抛出异常]或根本就[不会有返回值的函数表达式]或[箭头函数表达式的返回值类型] never类型是任何类 ...

  9. 清空elementui让计数器input-number的默认值

    <el-form-item label="考试时长:" prop="testTimeLong"> <el-input-number style ...

  10. 【分享代码片段】terraform中,如何从刚刚创建的 deployment 中获得所有容器的名字和 ip

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 不好意思,刚刚才开始用 terraform,或许是更好的办 ...