synchronized原理

本文深入解析Java中的synchronized关键字,包括其作用、底层原理、锁的状态及Java虚拟机的优化策略。探讨了synchronized与ReentrantLock的区别,帮助读者理解Java并发编程的核心机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作用

  1. 确保线程访问互斥的同步代码
  2. 确保共享变量的修改能及时可见
  3. 有效解决重排序问题

Java中的每一个对象都可以作为锁,这是synchronized的基础。

  • 普通同步方法,锁是当前实例对象
  • 静态同步方法,所示当前类的class对象
  • 同步方法块,锁是括号中的对象。

synchronize底层原理

同步代码块和同步方法实现的原理不同

同步代码块:通过添加monitorEnter和monitorExit指令来实现。(显示锁定)

同步方法:通过调用指令读取常量池方法表结构的ACC_SYNCHRONIZED标志实现。(隐式锁定)

任何一个对象都有一个monitor与之关联,当这个monitor被持有之后,处于锁定状态。

  • 对象在内存中的布局:

对象头:存放对象的运行时数据

实例数据:存放类的属性信息

对齐填充:字节对齐

用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等。

每一个线程都有一个可用的monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联。对象头markdown字段中的markword指向monitor的的起始地址。

monitor中的信息。

owner:表示持有该锁线程的唯一标识,没有线程持有时为空

entryQ:关联一个系统互斥锁,阻塞所有试图锁住monitor record失败的线程。

RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。

Nest:用来实现重入锁的计数。

HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。

Candidate:用来避免不必要的阻塞或等待线程唤醒。

  • Markdown字段的结构

锁的状态一共有四种:无锁、偏向锁、轻量级锁和重量级锁。锁的级别从低到高。

  • Java虚拟机对synchronize的优化
  • 偏向锁

如果一个线程获取了锁,那么锁就进入偏向模式,当这个线程再次请求锁时,无需做任何同步操作。

  • 轻量级锁

使用CAS操作来避免重量级锁使用互斥量的开销,CAS失败考虑转换为重量级锁

  • 锁消除

检测到不可能存在竞争的共享数据的锁进行锁消除

  • 自旋锁

让一个线程在请求一个共享数据的锁时,自旋(忙循环)一段时间,这样就避免了线程的状态切换,减小开销。

  • 锁粗化

一系列操作反复对同一个对象加锁,将加锁的范围扩展到着一些零碎操作的外部

  • synchronize和ReentrantLock的区别
  1. 实现:synchronize是Java中的关键字,是在JVM层面上实现的。ReentrantLock是JUC包中实现的锁。
  2. 加锁,解锁:synchronize在程序完成后会自动释放锁;ReentrantLock需要自己释放锁
  3. 异常:发生异常synchronize会自动释放锁,而ReentrantLock不会,需要手动在finally块中释放锁。
  4. 中断:synchronize不能响应中断,ReentrantLock可以响应中断,若长时间没获取锁可以放弃等待。
  5. 公平:synchronize是非公平锁,ReentrantLock默认是非公平锁,但是也有公平锁的实现,可以通过构造函数来构造公平锁
  6. 锁是否获取成功:synchronize不能知道锁是否获取成功。ReentrantLock可以通过lock.trylock()返回值知道锁是否获取成功。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值