Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >synchronized的偏向、轻量、重量级锁

synchronized的偏向、轻量、重量级锁

作者头像
青山师
发布于 2023-05-05 12:13:11
发布于 2023-05-05 12:13:11
26300
代码可运行
举报
运行总次数:0
代码可运行

synchronized的偏向、轻量、重量级锁

Synchronized实现同步的方式有三种:偏向锁、轻量级锁、重量级锁。本文会从理论和代码实践两方面阐述三种锁的实现细节和原理。

偏向锁

偏向锁的思想很简单,就是偏向于第一个获取锁的线程,当其他线程要获取锁时,会在CAS操作中失败,然后挂起等待,直到第一个线程释放锁。这个锁的好处是可以满足大多数同步场景下的需求,并且消耗很小的资源。

要开启偏向锁,需要添加JVM参数-XX:+UseBiasedLocking

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BiasedLocking {
    private Object lock = new Object();

    public void method1() {
        synchronized (lock) {
            // do something...
        }
    }
} 

当第一个线程进入synchronized块时,会将锁的标记从none修改为bias状态,同时记录偏向的线程ID。之后其他线程要获取锁,会通过CAS操作尝试将锁偏向自己,但这个操作会失败,所以只会短暂地竞争,很快其他线程就会进入阻塞状态,释放CPU时间片。

当偏向的线程退出同步块时,如果发现锁还没有其他线程在等待,那么会将锁的状态重置为none。如果发现有其他线程在等待,会释放锁,让等待线程获取。

当偏向的线程退出同步块时,如果发现锁还没有其他线程在等待,那么会将锁的状态重置为none。如果发现有其他线程在等待,会释放锁,让等待线程获取。

轻量级锁

轻量级锁的获取过程是通过CAS操作完成的。当前线程会先在对象头中记录自己,然后尝试用CAS将对象头中的锁记录替换为当前线程,如果成功就获取到锁,失败就进入阻塞队列等待唤醒。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class LightWeightLock {
    private Object lock = new Object();

    public void method1() {
        synchronized (lock) {
            // do something...
        }
    }
}

当第一个线程进入方法时,会将对象头中的锁状态修改为当前线程ID,然后进入同步块。其他线程要获取锁时,会先检查对象头的锁状态,如果发现锁已经被占用,那么会使用CAS操作进行抢占,如果成功则获取到锁,失败会加入到阻塞队列进行等待。

当前线程退出同步块时,会使用CAS操作释放锁,将对象头设置为unlocked状态,同时唤醒阻塞队列中的一个等待线程。

轻量级锁的优点是消耗资源小,对代码性能的影响小,但是在高并发的场景下,CAS操作的 ABA问题会导致线程无法正常工作,所以当锁重入超过10次,或者锁持有时间超过1s时,JVM会将轻量级锁升级为重量级锁。

重量级锁

重量级锁会导致当前拥有锁的线程和其他等待线程都进入阻塞状态,切换到内核态,这 obviously 是一个非常消耗资源的操作。

其实现过程是:当前线程首先会在对象头中记录自己,然后进入内核态被阻塞,同时其他线程也会被阻塞。当其中一个线程退出同步块时,会唤醒其他线程中的一个

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class HeavyWeightLock {
    private Object lock = new Object();

    public void method1() {
        synchronized (lock) {
            // do something...
        }
    }
}

当第一个线程进入同步块时,会标记对象头表示此对象处于锁定状态,然后进入内核态挂起。其他线程要获取锁时,会发现对象头的锁定状态,也会进入内核态挂起。

当锁定的线程退出同步块时,会标记对象头为解锁状态,然后唤醒一个等待线程。被唤醒的线程会重新标记对象头为锁定状态,然后继续执行同步块中的内容。

重量级锁的优点是可以解决轻量级锁中的ABA问题,但是其性能消耗也是最大的。所以如果一个锁仅被一个线程使用,或有很高的重入概率,那么应选择偏向锁或轻量级锁,可以获得更高的性能。

偏向锁适用于单线程环境,性能最高;轻量级锁通过CAS实现,性能较好,但是会出现ABA问题;

操作步骤

要实际观察Synchronized锁的三种状态转换,可以使用JDK自带的JMC(Java Mission Control)工具。

下面是具体的操作步骤:

  1. 启动JMC,打开“标记对象(Mark Objects)”功能。
  2. 设置需要追踪的对象,这里我们选择上面的Demo对象。
  3. 运行代码,多线程访问这个对象的同步方法。
  4. JMC中打开“标记对象(Mark Objects)”视图,可以观察到对象头的状态在变化:
    • 初始为none状态,表示无锁
    • 第一个线程进入同步块后变为biased状态,表示偏向锁定
    • 多线程访问后变为轻量级锁,对象头记录为线程ID
    • 重入超过10次或持有超过1s后,变为重量级锁,对象头记录为锁定状态
  5. 当线程退出同步块后,可以观察到锁的释放过程
    • 偏向锁会重置为none状态
    • 轻量级锁使用CAS设置为unlocked状态,并唤醒后继线程
    • 重量级锁设置对象头为unlocked状态,并唤醒后继线程
  6. 可以尝试提高并发量或设置不同的超时时间,观察偏向锁和轻量级锁什么情况下会升级为重量级锁

通过上述步骤,我们可以直观的观察Synchronized锁的三种状态之间的切换过程,这也是理解其原理的最佳途径。

运维实施

在实际项目中,我们如何根据场景选择和设置合适的锁机制呢?这里提供一些参考建议:

  1. 偏向锁:默认开启,适用于大多数轻 contention 的场景,可以通过-XX:-UseBiasedLocking关闭。
  2. 轻量级锁:默认开启,无需配置,在大部分场景下可以获得不错的性能,如果出现ABA问题,会自动升级到重量级锁。
  3. 重量级锁:无需配置,在以下场景会自动使用:
    • 偏向锁或轻量级锁升级
    • JVM发现轻量级锁CAS操作次数过高
    • 同步块内耗时较长(默认超过1s)
  4. 锁消除:可以通过-XX:+EliminateLocks开启,JVM会分析代码,消除不可能存在竞争的锁,以提高性能。
  5. 锁粗化:可以通过-XX:+DoEscapeAnalysis 开启,JVM会分析代码,将多个连续的加锁操作锁合并为一个,以减少加锁操作次数。
  6. 自旋锁:可以通过-XX:PreBlockSpin设置自旋次数,指令在获取锁失败先自旋一定次数,再进入阻塞状态,以减少线程切换。但是如果自旋太长,会消耗CPU,需要根据场景设置。
  7. 锁定超时:可以通过-XX:MonitorTimeout=x设置重量级锁定超时时间,以避免线程因锁定过长出现死锁现象。

除上述JVM层面的设置外,在代码层面我们也可以根据场景选择不同的锁来提高性能,比如使用ReentrantLock代替Synchronized等。

面试相关

理解Synchronized锁及其实现原理,是Java后端工程师的基础知识之一,这部分内容在面试中也常会涉及,下面是一些可能的面试题:

  1. Synchronized的作用是什么?它的实现原理是什么? 答:Synchronized关键字实现同步,使得运行在同一进程中的多个线程操作同步代码段(或方法)时是互斥的。它的实现方式有三种:偏向锁、轻量级锁、重量级锁。具体可以参考本文前述内容。
  2. 偏向锁、轻量级锁、重量级锁的优缺点分别是什么? 可以参考本文“总结”部分的内容。偏向锁资源消耗最少,单线程场景使用;轻量级锁性能较好,使用CAS实现,存在ABA问题;重量级锁性能最差但安全,用于阻塞线程和处理ABA问题。
  3. Synchronized如何进行锁升级? 当偏向锁被不同线程获取超过20次,或轻量级锁被不同线程获取超过10次、或持有时间超过1s,Synchronized会进行锁升级。升级规则如下:
    • 偏向锁升级为轻量级锁
    • 轻量级锁升级为重量级锁
    • 重量级锁不会再降级

    锁升级的目的是为了提高并发性能。偏向锁适用于单线程,升级为轻量级锁可以适应更高的并发;轻量级锁使用CAS有性能损耗,升级为重量级锁可以解决该问题。

  4. JDK1.6之前的Synchronized如何实现?现在的实现方式有何不同? 答:JDK1.6之前,Synchronized只有一种实现方式:重量级锁。 JDK1.6之后,引入了偏向锁和轻量级锁,实现方式更加灵活,可以根据场景选择,大大提高了并发性能。重量级锁会让线程进入阻塞状态,拥有锁的线程与其他线程都阻塞在内核态,资源消耗大。偏向锁和轻量级锁让线程可以在用户态取得锁,资源消耗小,性能更好。所以现代JDK的Synchronized实现方式相比以前更加智能化,可以根据实际场景选择合适的锁,以obtian更好的并发性能。
  5. 是否应该使用Synchronized优先于ReentrantLock?两者的区别是什么? 答:这两个都是可重入锁,用于实现同步功能。 Synchronized是JVM实现的,性能稍差但使用简单,自动释放锁,不会出现死锁风险。 ReentrantLock是JDK实现的,性能更好,可以设置公平锁、锁定超时时间等,但是使用不当会造成死锁,并且需要手动释放锁,否则可能导致资源泄漏。 所以是否应该优先使用,需要根据实际场景决定:
    • 简单同步场景,没有特殊要求,使用Synchronized简单可靠
    • 追求高性能或需要设置特殊锁策略,使用ReentrantLock
    • 资源竞争激烈,且同步块时间长,使用ReentrantLock并设置锁定超时时间可以避免死锁

    两者的主要区别如下:

    • Synchronized是隐式锁(内置语法),ReentrantLock是显式锁(API)
    • Synchronized无法设置锁定超时,ReentrantLock可以
    • Synchronized粗暴地让线程阻塞在内核态,ReentrantLock可以先自旋再阻塞
    • Synchronized适合少量同步代码段,ReentrantLock可以同时锁定多个同步资源
  6. 对锁的理解?锁主要有哪几种? 答:锁是用于实现同步的机制,保证共享资源被线程排他地访问。主要有以下几种锁:
    • 偏向锁:锁定一次后,后续的锁定由同一线程完成,适用于单线程或同一线程重复加锁的场景
    • 轻量级锁:使用CAS操作进行加锁,性能好但存在ABA问题,用于短期加锁
    • 重量级锁:进入内核态阻塞,其他线程也阻塞,性能差但安全,用于长期加锁
    • 自旋锁:获取锁失败后先自旋一定次数再阻塞,适用于锁定时间很短的场景,可以减少线程切换开销
    • 可重入锁:同一线程可以多次获取同一把锁,Synchronized和ReentrantLock都是可重入锁
    • 读写锁:读锁可以被多个线程同时获取,写锁是排他锁,在追求读写并发场景使用
    • 死锁:两个或两个以上线程分别占有一部分资源并等待其他资源,导致无限期等待,需要避免出现
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-05-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
老王:来了来了,小陈你准备好了吗?今天我们来讲synchronized的锁重入、锁优化、和锁升级的原理
终有救赎
2023/10/16
6470
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
Java Concurrent 偏向锁&轻量级锁&重量级锁
再说偏向锁之前先来看一下Java 对象头,Java 对象是分为 对象头、实例数据、对齐填充三部分,创建一个Java 对象所消耗和占用的cpu和内存代价都是很高的(尤其是对齐填充这一块,真的会浪费很多内存),和并发相关性最大的是对象头,因为Java 原生锁(sychronized)的信息是存放在Java 对象头中的。如果对象是数组类型,则虚拟机用3个Word(字宽)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。 对象头中的位数依赖于系统的位数: 1、32或64bit存放Mark Word,其中包括存储对象的hashCode或锁信息等。 2、32或64bit存放Class Metadata Address,也就是存储到对象类型数据的指针。 3、如果是数组对象的话,使用32或64bit存放Array length,也就是数组的长度)
邹志全
2019/07/31
7640
偏向锁、轻量级锁、重量级锁、自旋锁、自适应自旋锁
偏向锁就是在运行过程中,对象的锁偏向某个线程。即在开启偏向锁机制的情况下,某个线程获得锁,当该线程下次再想要获得锁时,不需要重新申请获得锁(即忽略synchronized关键词),直接就可以执行同步代码,比较适合竞争较少的情况。
一个会写诗的程序员
2020/05/26
4.8K0
Java锁---偏向锁、轻量级锁、自旋锁、重量级锁
之前做过一个测试,反复执行过多次,发现结果是一样的:  1. 单线程下synchronized效率最高(当时感觉它的效率应该是最差才对);  2. AtomicInteger效率最不稳定,不同并发情况下表现不一样:短时间低并发下,效率比synchronized高,有时甚至比LongAdder还高出一点,但是高并发下,性能还不如synchronized,不同情况下性能表现很不稳定;  3. LongAdder性能稳定,在各种并发情况下表现都不错,整体表现最好,短时间的低并发下比AtomicInteger性能差一点,长时间高并发下性能最高(可以让AtomicInteger下台了);
stys35
2020/03/28
2.1K3
偏向锁、轻量级锁、自旋锁、重量级锁,看这一篇就够了!
每一个刚接触多线程并发编程的同学,当被问到,如果多个线程同时访问一段代码,发生并发的时候,应该怎么处理?
终码一生
2022/04/14
2.2K0
偏向锁、轻量级锁、自旋锁、重量级锁,看这一篇就够了!
Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)
不止一次的提到过,synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的
noteless
2019/03/15
1K0
Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)
详解synchronized和锁升级,以及偏向锁和轻量级锁的升级
synchronized 是一个同步关键字,在某些多线程场景下,如果不进行同步会导致数据不安全,而 synchronized 关键字就是用于代码同步。什么情况下会数据不安全呢,要满足两个条件:一是数据共享(临界资源),二是多线程同时访问并改变该数据。
业余草
2021/12/06
1.4K0
详解synchronized和锁升级,以及偏向锁和轻量级锁的升级
锁升级:无锁、偏向锁、轻量级锁、重量级锁
JDK 1.6之前,synchronized 还是一个重量级锁,是一个效率比较低下的锁。但是在JDK 1.6后,JVM为了提高锁的获取与释放效率对synchronized 进行了优化,引入了偏向锁和轻量级锁 ,从此以后锁的状态就有了四种:无锁、偏向锁、轻量级锁、重量级锁。并且四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级,这四种锁的级别由低到高依次是:无锁、偏向锁,轻量级锁,重量级锁。如下图所示:
韩旭051
2021/04/14
3.2K0
锁升级:无锁、偏向锁、轻量级锁、重量级锁
面试官问你Java偏向锁如何膨胀到重量级锁
java对象头是实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的。
猿芯
2021/07/30
8360
面试官问你Java偏向锁如何膨胀到重量级锁
解"锁"疑惑:偏向锁为什么不是锁?锁升级又是什么?何时禁用偏向锁和轻量级锁?重量级锁怎么回事?
带着上面疑问,我们一起来解“锁”疑惑!以下是第二篇文章来讲,方便大家记忆!欢迎持续关注【程序视点】,这样就不会错过之后的精彩内容啦!
程序视点
2024/12/19
1030
解"锁"疑惑:偏向锁为什么不是锁?锁升级又是什么?何时禁用偏向锁和轻量级锁?重量级锁怎么回事?
详解Java多线程锁之synchronized
synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。
全菜工程师小辉
2019/08/16
3660
锁原理:偏向锁、轻量锁、重量锁1.加锁2.撤销偏向锁1.加锁2.解锁3.膨胀为重量级锁
 java中每个对象都可作为锁,锁有四种级别,按照量级从轻到重分为:无锁、偏向锁、轻量级锁、重量级锁。每个对象一开始都是无锁的,随着线程间争夺锁,越激烈,锁的级别越高,并且锁只能升级不能降级。 一、j
用户1174983
2018/02/05
4.3K0
锁原理:偏向锁、轻量锁、重量锁1.加锁2.撤销偏向锁1.加锁2.解锁3.膨胀为重量级锁
52.说一下 synchronized 底层实现原理?_synchronized底层实现
说起多线程同步,一般的方案就是加锁,而在 java 中,提到加锁就想起 juc 包提供的 Lock 接口实现类与默认的关键字 synchronized 。我们常听到,juc 下的锁大多基于 AQS,而 AQS 的锁机制基于 CAS,相比起 CAS 使用的自旋锁,Synchronized 是一种重量级的锁实现。
全栈程序员站长
2022/09/23
2.2K1
52.说一下 synchronized 底层实现原理?_synchronized底层实现
Synchronized实现原理与常见面试题
Synchronized 是常被我们用来保证临界区以及临界资源安全的解决方案。它可以保证当有多个线程访问同一段代码,操作共享数据时,其他线程必须等待正在操作线程完成数据处理后再进行访问。即 Synchronized 可以达到线程互斥访问的目的。
烂猪皮
2023/09/03
6320
Synchronized实现原理与常见面试题
学synchronized锁升级过程,吊打面试官
哈喽,大家好,我是IT老哥,我们今天来讲讲synchronized这个锁,可能你们第一印象是这个锁太笨了,太重了,谁用谁是傻子,如果你是这样想的话,那么面试的时候基本上就是凉凉了。因为jdk1.6之后对它进行了优化,它已经不是你们曾经认识的它了,中国有句古话,叫:士别三日当刮目相待,今天我们就来看看这个锁到底做了哪些优化。希望看到视频的你们动动小手给老哥点个赞。
公众号 IT老哥
2020/09/16
1.4K0
学synchronized锁升级过程,吊打面试官
JVM源码分析之synchronized1 字节码实现2 偏向锁
javap命令生成的字节码中包含 ** monitorenter ** 和 ** monitorexit **指令
JavaEdge
2018/09/20
1K0
JVM源码分析之synchronized1 字节码实现2  偏向锁
【java并发编程实战4】偏向锁-轻量锁-重量锁的那点秘密(synchronize实现原理)synchronized自旋锁偏向锁轻量锁重量锁小结
在多线程并发编程中,synchronized一直都是元老级别的角色,人们都通常称呼它为重量锁,但是在jdk1.6版本之后,jdk就对synchronized做了大量的优化,这时候我们就不能称呼它为重量锁了,有的时候它也是很轻的,那么接下来我们就调调,synchronized是怎么被优化的,它跟偏向锁、轻量锁、重量锁又有什么渊源。
yukong
2018/10/09
1.4K0
【java并发编程实战4】偏向锁-轻量锁-重量锁的那点秘密(synchronize实现原理)synchronized自旋锁偏向锁轻量锁重量锁小结
synchronized 与多线程的哪些关系
JVM 实现的 synchronized JDK 实现的 ReentrantLock
BUG弄潮儿
2022/06/30
2800
synchronized 与多线程的哪些关系
JUC并发编程01——谈谈锁机制:轻量级锁、重量级锁、偏向锁、锁消除与锁优化
计算机的cpu与I/O的效率并不是完全一致的,CPU的处理速度快时,在进行I/O操作时,可能会导致CPU空闲的状态,为了最打程度的利用cpu的资源,开发人员创造了并发编程,进程通过轮换可以最大程度的利用cpu的资源,同时给用户进程在同步执行的错觉。但是进程之间并不会共享数据,同时上下文的切换也比较耗时,线程横空出世,同一个进程中的不同线程之间内存共享一片内存区域,线程上下文切换也很轻量级。juc是java官方提供的线程操作的jar包,可以尽可能的降低我们并发编程的难度。
半旧518
2022/10/26
3860
JUC并发编程01——谈谈锁机制:轻量级锁、重量级锁、偏向锁、锁消除与锁优化
一文打通锁升级(偏向锁,轻量级锁,重量级锁)
在JavaSE1.6以前,synchronized都被称为重量级锁。但是在JavaSE1.6的时候,对synchronized进行了优化,引入了偏向锁和轻量级锁,以及锁的存储结构和升级过程,减少了获取锁和释放锁的性能消耗,有些情况下它也就不那么重了。
一个风轻云淡
2023/10/15
4130
一文打通锁升级(偏向锁,轻量级锁,重量级锁)
推荐阅读
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
6470
Java Concurrent 偏向锁&轻量级锁&重量级锁
7640
偏向锁、轻量级锁、重量级锁、自旋锁、自适应自旋锁
4.8K0
Java锁---偏向锁、轻量级锁、自旋锁、重量级锁
2.1K3
偏向锁、轻量级锁、自旋锁、重量级锁,看这一篇就够了!
2.2K0
Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)
1K0
详解synchronized和锁升级,以及偏向锁和轻量级锁的升级
1.4K0
锁升级:无锁、偏向锁、轻量级锁、重量级锁
3.2K0
面试官问你Java偏向锁如何膨胀到重量级锁
8360
解"锁"疑惑:偏向锁为什么不是锁?锁升级又是什么?何时禁用偏向锁和轻量级锁?重量级锁怎么回事?
1030
详解Java多线程锁之synchronized
3660
锁原理:偏向锁、轻量锁、重量锁1.加锁2.撤销偏向锁1.加锁2.解锁3.膨胀为重量级锁
4.3K0
52.说一下 synchronized 底层实现原理?_synchronized底层实现
2.2K1
Synchronized实现原理与常见面试题
6320
学synchronized锁升级过程,吊打面试官
1.4K0
JVM源码分析之synchronized1 字节码实现2 偏向锁
1K0
【java并发编程实战4】偏向锁-轻量锁-重量锁的那点秘密(synchronize实现原理)synchronized自旋锁偏向锁轻量锁重量锁小结
1.4K0
synchronized 与多线程的哪些关系
2800
JUC并发编程01——谈谈锁机制:轻量级锁、重量级锁、偏向锁、锁消除与锁优化
3860
一文打通锁升级(偏向锁,轻量级锁,重量级锁)
4130
相关推荐
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验