当前位置: 首页 > news >正文

synchronized 的使用和特性

synchronized 锁对象

普通方法
synchronized 锁普通方法时,其锁的对象是调用该方法的实例

public synchronized void method() {  // 方法体  
}  

静态方法
静态方法的锁对象是所属的 class,全局只有一个。

public static synchronized void staticMethod() {  // 方法体  
}  

同步代码块
锁对象为括号内的指定对象。

synchronized(this) {  // 代码块  
}  

synchronized 特性

有序性
读读,读写,写读,写写互斥。

可见性
可见性是指多个线程访问一个资源时,该资源的状态,值等对于其他线程都是可见的。synchronized 和 volatile 都具有可见性,其中 synchronized 对一个类或对象加锁时,一个线程如果要访问该累或对象必须先获得它的锁。这个锁的状态对于其他任何线程都是可见的,并且在释放锁之前会将对变量的修改刷新到共享内存中,保证资源变量的可见性。

原子性
原子性指的是同一时间只有一个线程去执行代码,该操作是不能被其他线程打断的,那么就具备了原子性。

synchronized的原子性本质上是线程互斥保证的原子性。

可重入性
同一线程在持有锁的情况下,可多次获取同一锁而不会导致死锁或阻塞其他线程。这种机制通过维护锁的持有计数器实现,当线程首次获取锁时计数器设为1,每次重入增加计数,释放时减少计数,直到归零才释放锁。

synchronized 锁升级的对象头内容:
在这里插入图片描述

偏向锁的意义和使用前提

偏向锁就是在运行过程中,对象的锁偏向某个线程。即在开启偏向锁机制的情况下,某个线程获得锁,当该线程下次再想要获得锁时,不需要重新申请获得锁(即忽略synchronized关键词),直接就可以执行同步代码,比较适合竞争较少的情况。

JDK 1.8 下加锁会默认开启偏向锁。但是它在应用程序启动几秒后才会开启,是存在延迟启动的情况。因此可能在打印输出加锁时的信息会发现不符合偏向锁的锁标志。

当我们开启了偏向锁,并且没有延迟开启的时候,新创建的对象的 mark word 默认就是偏向锁状态的 markWord,只不过这个时候,因为没有线程争抢锁,除了我们的锁标志位和是否为偏向锁标志位,其他都是 0

延迟的关闭和偏向锁的关闭

延迟是可以关闭的。可以给 JVM 设置参数:-XX:BiasedLockingStartupDelay=0 来关闭延迟。
如果希望关闭偏向锁:-XX:-UseBiasedLocking=false

偏向锁细节

无锁状态下的 MarkWord 标志(看第一行的二进制数字):
01 表示偏向锁,是由于开启偏向锁且没有延迟开启的情况下会显示的,但是此时并没有线程争夺锁。因此其他位置都是 0 ,仅仅是锁标志位和是否为偏向锁标志位有变化。
在这里插入图片描述
线程加上偏向锁后:
根据上图,偏向锁加上了后会有标识线程 ID,Epoch 等信息,因此不全是 0 。
在这里插入图片描述

如果我们在加锁前调用 hashcode 方法,会导致后续加锁后变为轻量级锁。

原理:

  • 被加锁的对象,没有真正调用或者隐式的调用(比如使用 HashMap 放入当前对象,会调用 HashCode 方法)父类 Object 的 hashCode 方法,如果一旦调用了 hashCode 方法,对象头里需要有一个存储该 hashCode 值的位置。但是我们可以从上图中看到,偏向锁中并没有地方进行 MarkWord 的保存,只有轻量级锁才会有。

  • 为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID(对象头:存储线程 ID,栈帧的锁记录中:线程有自己的栈帧,LOCK RECORD: 存储当前线程 ID),以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。(是否是当前线程的偏向锁是通过ID来匹配的)

  • 如果测试成功,表示线程已经获得了锁。

  • 如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程(CAS 竞争,替换线程 ID)

http://www.dtcms.com/a/269596.html

相关文章:

  • 算法学习笔记:11.冒泡排序——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • VBA经典应用69例应用8:取消预设任务
  • (三)C#使用yolo
  • 在教育领域中,如何通过VRM分片错序对视频进行加密?
  • git学习:首次创建仓库
  • ubuntu 运行脚本打开WIFI adb
  • YOLO在自动驾驶交通标志识别中的应用与优化【附代码】
  • Qt:图片切割
  • 代码详细注释:演示如何使用dup()系统调用复制文件描述符
  • Linux操作系统:再谈虚拟地址空间
  • const char* 、char*和char[]的区别
  • MySQL数据库访问(C/C++)
  • 恒创科技:香港站群服务器做seo站群优化效果如何
  • 2025年数据挖掘与计算机科学国际会议 (DMCS 2025)
  • 基于Docker Compose部署Traccar容器与主机MySQL的完整指南
  • 专题:2025数据资产AI价值化:安全、战略与应用报告|附400+份报告PDF、原数据表汇总下载
  • uniapp 监听物理返回按钮
  • 分水岭算法:图像分割的浸水原理
  • 视频号账号矩阵运营中定制开发开源 AI 智能名片 S2B2C 商城小程序的赋能研究
  • 【王树森推荐系统】召回11:地理位置召回、作者召回、缓存召回
  • 【Rust base64库】Rust bas64编码解码详细解析与应用实战
  • ​​​​​​​营销费用管理,如何驱动快消企业营销投资战略升级
  • 萌新赛第(一)场
  • IEEE Fellowe助力 2025年物联网、数据科学与先进计算国际学术会议(IDSAC2025)
  • C++——string的了解和使用
  • 将oracle表字段json字符串分解提取并返回单列表
  • Redis基础数据结构
  • 深度学习与图像处理 | 基于传统图像处理的自动驾驶车道线检测
  • XSLT注入与安全修复方法
  • 【快手】数据挖掘面试题0002:求某地铁站每日客流量,乘地铁经过、进出站人都包括在内