偏向锁的原理与实战

article/2025/10/21 13:16:56

文章目录

    • 1. 偏向锁的核心原理
    • 2. 偏向锁代码演示
    • 3. 偏向锁的膨胀与撤销

1. 偏向锁的核心原理

如果不存在线程竞争的一个线程获得了锁,那么锁就进入偏向状态,此时Mark Word的结构变为偏向锁结构,锁对象的锁标志位(lock)被改为01,偏向标志位(biased_lock)被改为1,然后线程的ID记录在锁对象的Mark Word中(使用CAS操作完成)。以后该线程获取锁时判断一下线程ID和标志位,就可以直接进入同步块,连CAS操作都不需要,这样就省去了大量有关锁申请的操作,从而也就提升了程序的性能。

关键点:无竞争

缺点:如果锁对象时常被多个线程竞争,偏向锁就是多余的,并且其撤销的过程会带来一些性能开销


2. 偏向锁代码演示

偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数
-XX:BiasedLockingStartupDelay=0 来禁用延迟

package innerlock;import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;public class InnerLockTest {int a=1;double b=1.1;public static void main(String[] args) {System.out.println(VM.current().details());Person person=new Person();ClassLayout layout=ClassLayout.parseInstance(person);new Thread(()->{System.out.println("获取偏向锁前:");System.out.println(layout.toPrintable());synchronized (person) {System.out.println("获取偏向锁中:");System.out.println(layout.toPrintable());}System.out.println("获取偏向锁结束后:");System.out.println(layout.toPrintable());},"thread1").start();}}
class Person{}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

禁用偏向锁:添加 VM 参数 -XX:-UseBiasedLocking
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


3. 偏向锁的膨胀与撤销

假如有多个线程来竞争偏向锁,此对象锁已经有所偏向,其他的线程发现偏向锁并不是偏向自己,就说明存在了竞争,尝试撤销偏向锁(很可能引入安全点),然后膨胀到轻量级锁

1. 偏向锁的撤销

  1. 在一个安全点停止拥有锁的线程
  2. 遍历线程的栈帧,检查是否存在锁记录。如果存在锁记录,就需要清空锁记录,使其变成无锁状态,并修复锁记录指向的Mark Word,清除其线程ID
  3. 将当前锁升级成轻量级锁
  4. 唤醒当前线程

撤销偏向锁的条件(满足其一即可):

  1. 多个线程竞争偏向锁
  2. 调用偏向锁对象的hashcode()方法或者System.identityHashCode()方法计算对象的HashCode之后,将哈希码放置到Mark Word中,内置锁变成无锁状态,偏向锁将被撤销

2. 批量重偏向与撤销

批量重偏向解决的问题:
一个线程创建了大量对象并执行了初始的同步操作,之后在另一个线程中将这些对象作为锁进行之后的操作。这种case下,会导致大量的偏向锁撤销操作。

package innerlock;import java.util.ArrayList;import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;public class InnerLockTest {int a=1;double b=1.1;public static void main(String[] args) throws InterruptedException {System.out.println(VM.current().details());ArrayList<Person> list=new ArrayList<Person>();new Thread(()->{for(int i=0;i<100;i++){Person person=new Person();synchronized (person) {list.add(person);}}try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}},"thread1").start();Thread.sleep(3000);new Thread(()->{for(int i=0;i<30;i++){Person person=list.get(i);synchronized (person) {if(i==17||i==18||i==19||i==21){System.out.println("第"+(i+1)+"次偏向结果:");System.out.println(ClassLayout.parseInstance(person).toPrintable());}}}},"thread2").start();}}
class Person{}

在这里插入图片描述在这里插入图片描述结果分析:
先用线程1创建了100个对象锁,这些对象锁都偏向于线程1,后面创建线程2去争夺这些锁,前19次线程2都是抢占失败获得轻量级锁(失败过程中阈值增加),第20次抢占时达到阈值20,这时JVM会认为自己是不是不应该偏向线程1,于是之后开始偏向线程2,线程2之后获得的都是偏向锁

  • 第1-19个对象由于线程2在抢占过程中变为轻量级锁,锁释放后变为无锁状态
  • 第20-30个对象触发批量重定向,锁释放后依旧偏向线程2
  • 第31-100个对象依然和开始一样偏向线程1,锁释放后依旧偏向线程1

批量撤销解决的问题:
存在明显多线程竞争的场景下使用偏向锁是不合适的,例如生产者/消费者队列

package innerlock;import java.util.ArrayList;import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;public class InnerLockTest {int a=1;double b=1.1;public static void main(String[] args) throws InterruptedException {System.out.println(VM.current().details());ArrayList<Person> list=new ArrayList<Person>();new Thread(()->{for(int i=0;i<100;i++){Person person=new Person();synchronized (person) {list.add(person);}}try {//为了防止JVM线程复用,在创建完对象后,保持线程t1状态为存活Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}},"thread1").start();Thread.sleep(3000);new Thread(()->{for(int i=0;i<40;i++){Person person=list.get(i);synchronized (person) {if(i==18||i==19||i==39||i==41){System.out.println("t2  第"+(i+1)+"次偏向结果:");System.out.println(ClassLayout.parseInstance(person).toPrintable());}}}try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}},"thread2").start();Thread.sleep(3000);new Thread(()->{for(int i=20;i<40;i++){Person person=list.get(i);synchronized (person) {if(i==20||i==39){System.out.println("t3   第"+(i+1)+"次偏向结果:");System.out.println(ClassLayout.parseInstance(person).toPrintable());}}}},"thread3").start();Thread.sleep(1000);System.out.println("新创建对象:"+ClassLayout.parseInstance(new Person()).toPrintable());}}
class Person{}

在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

做法:
以class为单位,为每个class维护一个偏向锁撤销计数器,每一次该class的对象发生偏向撤销操作时,该计数器+1,当这个值达到重偏向阈值默认20时,JVM就认为该class的偏向锁有问题,因此会进行批量重偏向。每个class对象会有一个对应的epoch字段,每个处于偏向锁状态对象的mark word中也有该字段,其初始值为创建该对象时,class中的epoch的值。每次发生批量重偏向时,就将该值+1,同时遍历JVM中所有线程的栈,找到该class所有正处于加锁状态的偏向锁,将其epoch字段改为新值。下次获得锁时,发现当前对象的epoch值和class的epoch不相等,那就算当前已经偏向了其他线程,也不会执行撤销操作,而是直接通过CAS操作将其mark word的Thread Id 改成当前线程Id

当达到重偏向阈值后,假设该class计数器继续增长,当其达到批量撤销的阈值后(默认40),JVM就认为该class的使用场景存在多线程竞争,会标记该class为不可偏向,之后,对于该class的锁,直接走轻量级锁的逻辑

总结:
在这里插入图片描述

3. 偏向锁的膨胀
如果偏向锁被占据,一旦有第二个线程争抢这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到内置锁偏向状态,这时表明在这个对象锁上已经存在竞争了。JVM检查原来持有该对象锁的占有线程是否依然存活,如果挂了,就可以将对象变为无锁状态,然后进行重新偏向,偏向抢锁线程。如果JVM检查到原来的线程依然存活,就进一步检查占有线程的调用堆栈是否通过锁记录持有偏向锁。如果存在锁记录,就表明原来的线程还在使用偏向锁,发生锁竞争,撤销原来的偏向锁,将偏向锁膨胀(INFLATING)为轻量级锁


参考:
《Java高并发编程卷2》 尼恩
https://www.cnblogs.com/LemonFive/p/11248248.html


http://chatgpt.dhexx.cn/article/XcnjIYYL.shtml

相关文章

Java并发 | 19.[锁机制] 偏向锁(CAS)

文章目录 1. 偏向锁分析回顾轻量级锁偏向锁的优势 2. 偏向锁&#xff08;CAS&#xff09;2.1. 偏向锁流程概述2.2. 锁升级 参考资料 1. 偏向锁分析 回顾轻量级锁 在上文 Java并发 | 18.[锁机制] 轻量级锁&#xff08;CAS自旋锁&#xff09;中对轻量级锁进行过解析&#xff0c…

Synchronized-偏向锁

偏向锁是什么&#xff1f; 是jdk1.6引入的一种锁优化方式。让 锁对象 偏心于第一次获取锁的线程&#xff0c;记住它的id&#xff0c;当下一次再有线程获取锁的时候&#xff0c;与记录的ID匹配&#xff0c;直接获取锁就行。是一种load-and-test的过程。 引入目的&#xff1f; …

面试题-- 什么是偏向锁

所谓的偏向&#xff0c;就是偏心&#xff0c;即锁会偏向于当前已经占有锁的线程 。 大部分情况是没有竞争的&#xff08;某个同步块大多数情况都不会出现多线程同时竞争锁&#xff09;&#xff0c;所以可以通过偏向来提高性能。即在无竞争时&#xff0c;之前获得锁的线程再次获…

Java中的偏向锁,轻量级锁, 重量级锁解析

文章目录 参考文章Java 中的锁一些先修知识synchronized 关键字之锁的升级&#xff08;偏向锁->轻量级锁->重量级锁&#xff09;无锁 -> 偏向锁偏向锁的撤销&#xff08;Revoke&#xff09;偏向锁的批量再偏向&#xff08;Bulk Rebias&#xff09;机制偏向锁 -> 轻…

条件分布

1.离散型&#xff1a; 例1&#xff1a; 2.连续型&#xff1a;

关于多元正态分布的条件分布的证明

之前在机器学习 cs229学习笔记3 (EM alogrithm,Mixture of Gaussians revisited & Factor analysis )中直接给出了多元正态分布的条件概率 正好今天上课讲了多元正态分布的内容&#xff0c;但没有涉及条件概率&#xff0c;所以下来baidu了一下&#xff0c;找到一个不错的pp…

条件分布函数(离散-条件分布律)

一、离散随机变量的条件分布函数 1.0、条件分布函数定义 1.1、例 1.1.1、例1 1.1.2、例2 二、连续型随机变量的条件分布函数 2.1、连续随机变量的条件分布函数 2.2、条件概率密度函数

第三章 多维随机变量及其分布 3.3 条件分布

第三章 多维随机变量及其分布 3.3 条件分布 文章目录 第三章 多维随机变量及其分布 3.3 条件分布离散型随机变量的条件分布连续型随机变量的条件分布 离散型随机变量的条件分布 例&#xff1a; 求&#xff1a; 离散型的直接套条件概率的公式就行了。 连续型随机变量的条件分布…

~《概率论》~条件分布

《概率论》条件分布 文章目录 ~《概率论》~条件分布一、离散型随机向量的条件分布1.1、定义1.2、性质 二、连续型随机向量的条件分布2.1、定义2.2、性质 一、离散型随机向量的条件分布 1.1、定义 1.2、性质 二、连续型随机向量的条件分布 2.1、定义 2.2、性质

MCMC算法--Gibbs采样2:多元高斯分布的边际分布与条件分布推导

因为在下篇博客中会介绍Gibbs采样&#xff0c;代码示例用到的是多元高斯分布&#xff0c;所以对条件分布&#xff0c;边际分布公式必须写出来&#xff0c;所以博主整理了下。 参考文献为&#xff1a;http://fourier.eng.hmc.edu/e161/lectures/gaussianprocess/node7.html 由…

条件高斯分布

多元高斯分布的一个重要性质是如果两个变量集是联合高斯分布&#xff0c;那么其中一个基于另一个变量集上的条件分布仍然是高斯分布。边缘高斯分布也有类似结论。 考虑第一种情形的条件高斯分布。假设 X 是一个满足高斯分布 的 D 维向量&#xff0c;我们把 X 分作两个子集 Xa 和…

【概率论】3-6:条件分布(Conditional Distributions Part I)

原文地址1&#xff1a;https://www.face2ai.com/Math-Probability-3-6-Conditional-Distributions-P1转载请标明出处 Abstract: 首先介绍随机变量的条件分布&#xff0c;随后介绍随机变量条件分布下的乘法法则&#xff0c;贝叶斯公式和全概率公式 Keywords: Discrete Conditio…

概率论与数理统计(3.3)二维随机变量条件分布

这个条件分布主要只针对二维的 一、离散型随机变量的条件分布 同理固定一个X为一个常数则可得Y的条件分布律 **注&#xff1a;**离散型的求在什么条件下X或Y的条件分布律&#xff0c;知道他们的联合分布律很重要. 1&#xff09; 观察这个公式。 注&#xff1a;必须知道P{X1}的…

多元正态分布条件分布公式总结

假设X是有两个随机向量组成 其中 假设X服从多元高斯分布其中 多元正态分布

【概率论】二维随机变量:联合分布律、边缘分布律和条件分布律之间的关系

摘要&#xff1a;本文主要介绍二维随机变量的联合分布律、边缘分布律和条件分布律之间的关系&#xff0c;并以矿山事故为例&#xff0c;强化对三者关系的认识。 一、联合分布律、边缘分布律和条件分布律之间的关系 1、若已知(X, Y)的联合分布率 > 可以求出X的边缘分布律和…

用最简洁的方法证明多元正态分布的条件分布

在证明高斯-马尔科夫随机过程的性质的过程中&#xff0c;遇到了多元正态分布的条件分布的证明&#xff0c;百度发现条件分布的很多证明方法写的极其麻烦&#xff0c;所以自己写了一个。 实际上多元随机变量的公式证明一般用矩阵的方法处理&#xff0c;这里就是采用这种方法&…

联合分布、条件分布、边缘分布还有贝叶斯公式

https://blog.csdn.net/zgcr654321/article/details/85201614?utm_mediumdistribute.pc_relevant.none-task-blog-OPENSEARCH-1.nonecase&depth_1-utm_sourcedistribute.pc_relevant.none-task-blog-OPENSEARCH-1.nonecase 文章目录 联合概率及其分布、边缘概率及其分布、…

概率论-3.5 条件分布与条件期望

离散随机变量的条件分布&#xff1a; 二位随机变量(X,Y)的联合分布列&#xff1a;pijP(Xxi,Yyj) P(Yyj)Sum(P(Xxi,Yyj))&#xff0c;对xi累加 P(Xxi)Sum(P(Xxi,Yyj))&#xff0c;对yj累加 条件分布列&#xff1a; pi | j P(Xxi | Yyj)P(Xxi,Yyj) / P(Yyj)pij / pj pj | i P(Yyj…

3.3 二维随机变量条件分布

学习目标&#xff1a; 要学习二维随机变量的条件分布&#xff0c;我可能会采取以下步骤&#xff1a; 复习边缘分布和联合分布&#xff1a;首先需要了解二维随机变量的边缘分布和联合分布的概念以及相应的公式。 复习条件概率&#xff1a;学习条件概率的定义和计算公式&#x…