Synchronized-偏向锁

article/2025/10/21 15:36:43

偏向锁是什么?

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

引入目的?

基础库中很多类为了线程安全,添加了synchronized进行同步,保证线程安全。很多时候,我们在使用这些类的对象时,都是单线程进行访问。轻量级锁的获取锁和释放锁涉及多次CAS操作,而偏向锁只依赖一次CAS。比轻量级锁更加轻量。

JDK1.6默认开启,JDK15后默认关闭,可以通过-XX:-UseBiasedLocking进行配置启用还是禁用。

优点

  • 加锁解锁无需额外的消耗,和非同步方法时间相差纳秒级别

缺点

  • 如果存在多个线程同时访问该锁对象,则会带来额外的锁撤销的消耗。

对象状态

以下状态默认为开启偏向锁配置

对象创建

当开启偏向锁时,类的prototype_header中的锁标志位为01,是否可偏向locked_bias1,thread id为0,表示匿名偏向(anonymously biased)。

出于性能的考虑,在JVM启动后的头4秒,偏向锁配置是关闭的,locked_bias0,以禁止实例化对象被偏向。4秒后,prototype_header.locked_bias会重新设置为1,新的对象就可以被偏向绑定了。

可通过-XX:BiasedLockingStartupDelay=0将延迟改为0。即偏向锁设置在JVM一启动就生效。

锁状态

未绑定不可偏向

此时locked_bias为0,如果有线程获取此锁,会升级为轻量级锁,如果有多个线程尝试获取此锁,会升级成重量级锁。

什么时候会变成此状态呢?

  • 计算对象的hashCode,调用Object.hashCodeSystem.identityHashCode(Object)
    • 对象的hashCode并不是一创建就计算好的,而是在调用hashCode方法后,才存储在对象头中的。
    • 一旦对象进行hashCode计算,都会从可偏向状态转为此状态。如果正在偏向,则会发生锁撤销操作。
  • 偏向锁被禁用
    • jvm参数-XX:-UseBiasedLocking将偏向锁禁用
    • jvm启动头4秒
    • 发生批量锁撤销后
      • 当该类所有对象发生锁撤销的次数达到40次后,会触发批量锁撤销。将prototype_header中的locked_bias设置为0,并对所有该类对象触发锁撤销操作。以后新创建的对象,都是不可偏向的状态。
  • 偏向锁被撤销
    • 当锁对象已偏向某个线程,此时有另外一个线程来访问该锁对象,且不可重偏向则会发生锁撤销。

    • 批量锁撤销时

可偏向

locked_bias1

匿名偏向

Mark Word中的Thread ID偏向线程为0时,为匿名偏向。

意味着该锁还没有偏向于任何线程。第一个试图获得锁的线程,使用CAS指令就可将线程绑定在此锁对象上。

这是允许偏向对象的初始状态。

已偏向

Thread ID不为空,且对象头的Mark Word.epochKlass._prototype_header.epoch相等。表示当前锁已偏向于某个线程,并且不可重偏向。如果当前线程不是锁对象偏向的线程,则触发锁撤销。升级成轻量级锁。

可重偏向

Thread ID不为空,但是对象头的Mark Word.epochKlass._prototype_header.epoch不相等。代表当前锁对象的偏向已过期,可进行重偏向。使用CAS将新线程绑定于当前锁对象。

加锁过程

  1. 验证Mark Word.locked_bias
    1. 0,不可偏向,升级为轻量级锁
  1. 验证Klass.prototype_header.locked_bias
    1. 0,则该类所有对象全部不允许偏向,需要重置该类所有对象的locked_bias位,该类所有对象Mark Word替换为无锁标识,当前对象升级为轻量级锁。
  1. 比较对象和原型epoch位,即Mark Word.epoch == Klass._prototype_header.epoch
    1. 不想等,代表该锁的偏向已过期,可进行重偏向。进行重偏向获取偏向锁。
    2. 相等,代表该锁已偏向某个线程。继续第4步重入或者升级为轻量级锁
  1. 检验owner线程
    比较偏向线程id和当前线程id
    1. 匹配,进行重入锁,向当前线程栈中写入一条Displaced Mark Word为空,Owner指向锁对象的Lock Record
    2. 不匹配,使用CAS进行线程替换(0->当前线程)。如果替换失败,需要进行线程撤销,替换Mark Word为无锁状态,并升级为轻量级锁。根据偏向线程是否还在执行同步代码块,判断是哪个线程获取升级后的轻量级锁。
      1. 偏向线程正在执行同步代码块,则偏向线程获取该锁。
      2. 偏向线程未处于活动状态或退出同步代码块,则当前线程获取轻量级锁。

解锁过程

偏向锁加锁,会向获取到锁的线程栈中插入一条Lock Record,其中的属性Displaced Mark Word为null,Owner指向当前锁对象。

偏向锁的解锁,是将线程栈中的Lock Record.Owner设置为null即为解锁。

锁撤销

  1. Mark word设置为无锁状态,并升级为轻量级锁。
    1. 如果偏向线程还在执行同步代码块,则偏向线程获取该轻量级锁,当前线程进行轻量级锁升级重量级锁的自旋等待。
    2. 如果偏向线程已执行完同步代码块或未处于活动状态,则当前线程获取升级后的轻量级锁。

批量重偏向

bulk rebias

避免短时间内大量偏向锁的撤销。例如一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,这样会导致大量的偏向锁撤销操作。当执行批量重偏向后,如果原偏向锁持有者线程不再执行同步块,则锁可以偏向于新线程。

Klass中维护了一个偏向锁撤销计数器revocation_count,每次该类的对象发生偏向锁撤销操作时,该计数器+1,当达到阈值20时,JVM会认为该偏向锁存在问题,给该偏向锁一个重偏向的机会。将Klass.prototype_header.epoch+1。并会对该类的所有对象的偏向状态进行初始化。

  • 如果该对象的偏向线程当前持有该锁对象,则更新mark word.epoch=_prototype_header.epoch,表示该锁对象已偏向。
  • 其他对象不更新,表示该对象偏向已过期,可进行重新偏向。

批量锁撤销

revocation_count达到阈值40时,JVM认为该偏向锁,存在多线程竞争关系。

但是在彻底禁用偏向锁之前,JVM还给了一次改过自新的机会,存在另外一个计时器BiasedLockingDecayTime = 25000

  1. 如果在距离上次批量重偏向发生的 25 秒之内,并且累计撤销计数达到40,就会发生批量撤销(偏向锁彻底 game over)
  2. 如果在距离上次批量重偏向发生超过 25 秒之外,那么就会重置在[20,40)内的计数, 再给次机会。

禁用偏向锁过程:更新klass._prototype_header.locked_bias为0,之后对于该class对象的锁,直接走轻量级锁逻辑。

如何查询Mark Word

  1. openjdk提供了一个库查看内存布局工具JOL(Java object layout)JOL maven 仓库地址
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.16</version>
</dependency>
复制代码
  1. 其中ClassLayout.parseInstance(o).toPrintable()可以打印出Mark word信息。查看锁状态。

示例

package com.learn.project;import org.openjdk.jol.info.ClassLayout;/*** MarkWordTest** @author chenfuyuan* @date 2022/12/10 19:01*/
public class MarkWordTest {public static void main(String[] args) throws InterruptedException {User initBeforeBiasSetting = new User();System.out.println("设置可偏向之前:"+ printClassLayout(initBeforeBiasSetting));Thread.sleep(5000L);User initAfterBiasSetting = new User();System.out.println("设置可偏向之后:"+ printClassLayout(initAfterBiasSetting));synchronized (initAfterBiasSetting) {//进入同步代码块System.out.println("进入同步代码块,进行偏向绑定:" + printClassLayout(initAfterBiasSetting));}Thread thread01 = new Thread(() -> {synchronized (initAfterBiasSetting) {System.out.println("被另外一个线程调用后(thread):" + printClassLayout(initAfterBiasSetting));}});thread01.start();Thread.sleep(1000L);System.out.println("被另外一个线程调用后(main):" + printClassLayout(initAfterBiasSetting));}private static String printClassLayout(Object obj) {return ClassLayout.parseInstance(obj).toPrintable();}
}===================输出=================
/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/bin/java -javaagent:/Users/chenfuyuan/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/223.7571.182/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=58794:/Users/chenfuyuan/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/223.7571.182/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_333.jdk/Contents/Home/jre/lib/rt.jar:/Users/chenfuyuan/code/learn/java/learn-jdk/learn-juc/target/classes:/Users/chenfuyuan/resource/maven_repository/learn/org/openjdk/jol/jol-core/0.16/jol-core-0.16.jar com.learn.project.MarkWordTest
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
设置可偏向之前:com.learn.project.User object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf800c14312   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total设置可偏向之后:com.learn.project.User object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0)8   4        (object header: class)    0xf800c14312   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total进入同步代码块,进行偏向绑定:com.learn.project.User object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x00007fe28f809005 (biased: 0x0000001ff8a3e024; epoch: 0; age: 0)8   4        (object header: class)    0xf800c14312   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total被另外一个线程调用后(thread):com.learn.project.User object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x00000003061f89e8 (thin lock: 0x00000003061f89e8)8   4        (object header: class)    0xf800c14312   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total被另外一个线程调用后(main):com.learn.project.User object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf800c14312   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalProcess finished with exit code 0
复制代码

注意事项

  1. 必须调用Object.hashCode才会进行锁撤销。重载的hashCode方法无法触发锁撤销。
  2. 在同步代码块中调用hashCode,锁对象会直接升级成重量级锁。

待补充的知识

  • 轻量级锁
  • 重量级锁
  • 安全点 safe point

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

相关文章

面试题-- 什么是偏向锁

所谓的偏向&#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…

NPOI 下载

https://www.nuget.org/packages/NPOI/

NPOI 第一篇 NPOI的下载、引用、基本使用

前言 总渴望去拥有那些自己没有的东西&#xff0c;比如经验、能力&#xff1b;但自己拥有的更为重要的东西&#xff0c;却如此的不珍惜&#xff0c;比如青春。所以啊&#xff0c;趁着青春多写点代码&#xff0c;就能让自己早一步去渴望重新拥有青春了。 有需要的小伙伴可以直…