synchronized实现原理详解

Java对象头:
若对象为数组,则虚拟机使用3个字宽存储对象头,否则,使用2个字存储对象头(64位虚拟机1字为8字节,64bit; 32虚拟机1字为4字节,32bit),具体如下图2-2所示:
在这里插入图片描述
其中Mark Word中存储着对象的HashCode、分代年龄、锁标记位。32位虚拟机的Mark Word如下图2-3所示(默认情况下):
在这里插入图片描述
运行期间,Mark Word中存储的数据可能会变化为下面四种:
在这里插入图片描述
注意:当要使用到对象的hashCode时,才会生成对象的hash码,这时因为要存储hashCode,对象的偏向状态将会丢失。

锁的升级与对比:
Java1.6中锁一共有四种状态:级别由低到高为:无锁,偏向锁,轻量级锁,重量级锁。锁可以升级但不能降级,目的是为了提高获得锁和释放锁的效率。

1、偏向锁:
引入原因:大多数情况下,锁总是由同一个线程多次获得。
实现细节:当一个线程尝试访问同步块获取锁时,会在
对象头
栈帧中的锁记录中记录锁偏向的线程id,此后,该线程进入和退出同步块时只需测试一下对象头的Mark Word是否记录着指向当前线程的偏向锁,若是则成功获取锁。否则看Mark Word中偏向锁标识是否为1,若不是,则尝试CAS竞争锁,若是则尝试CAS将对象头的偏向锁指向当前线程。
偏向锁的撤销:
1、出现锁竞争
当出现锁竞争时,拥有偏向锁的线程才会释放锁,释放偏向锁需要等待全局安全点(没有正在执行的字节码指令),首先需要暂停偏向的线程,然后检查偏向的线程是否活着,若活着,则遍历偏向对象的锁记录,栈中锁记录和对象头的Mark Word要么重新偏向其他线程,要么恢复无锁状态或进行锁升级。
2、调用锁对象的HashCode
偏向锁的hashcode存储在锁对象的MardWord中,当没有使用到对象的hashcode时,hashcode不生成,当使用了对象的hashcode,hashcode要占用markwork中25bit,就会覆盖锁对象偏向的线程id。(轻量级锁会在锁记录中记录hashCode,重量级锁会在Monitor中记录hashCode)
3、持有锁的线程调用wait/notify。
偏向锁的获得和撤销如下图所示:
在这里插入图片描述
偏向锁默认是延迟启动的,如果要关闭延迟::-XX:BiasedLockingStartupDelay=0。如果要关闭偏向锁:-XX:-UseBiasedLocking=false。关闭后,程序默认会进入轻量级锁状态。

批量重偏向
如果对象虽然被多个线程访问,但没有竞争,这时偏向了T1的对象仍有机会重新偏向T2,重偏向会重置锁对象的ThreadID;
当撤销偏向锁阈值超过20次后,jvm会这样觉得,是不是偏向错了,于是会在给这些对象加锁时重新偏向至加锁线程;
批量撤销
当撤销偏向锁阈值超过40次,jvm会觉得,自己确实错了,根本不该偏向。于是整个类所有对象都会变为不可偏向的,新建的对象也是不可偏向的;
锁消除
内部对象加锁时,java即时编译器(JIT)会把synchronized优化掉(不存在竞争),可以使用 -XX:-EliminateLocks关闭。

2、轻量级锁:
加锁:
尝试获取锁之前,当前线程会先在栈帧中创建锁记录,并将对象头中Mark Word复制到锁记录中,然后cas将对象头的mark word 替换为指向锁记录的指针,若成功,则获得锁,否则尝试自旋获取锁。
解锁:
cas将锁记录中mark word替换回所对象中(之前所对象的mark word换为了指向锁记录的指针),若成功,则解锁成功,否则则存在竞争,锁会膨胀为重量级锁。流程如下:
在这里插入图片描述

总结:
在这里插入图片描述
(本文参考Java并发编程的艺术)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
synchronizedJava实现线程同步的关键字之一。它是通过Java对象头的Monitor来实现的。Java对象头包含了一些与线程同步相关的信息,其就包括了一个指向持有该对象的线程的指针。当一个线程尝试获取一个对象的时,它首先会检查对象头的Monitor信息,判断是否已经有线程持有该对象。如果没有,则该线程可以获取,并将自己的线程ID保存在对象头的Monitor。如果有其他线程已经持有该对象,那么当前线程就会进入阻塞状态,等待其他线程释放。 此外,synchronized还实现了的可重入性。可重入意味着当一个线程已经持有了一个对象后,再次请求该对象时,仍然可以获得该。这样就避免了线程自己因为已经持有而被阻塞。 另外,synchronized还保证了线程执行的有序性。尽管编译器和处理器为了提升代码效率可能进行指令重排序,但synchronized关键字保证了被修饰的代码在同一时间内只能被同一线程访问。这样就保证了被修饰的代码块的执行顺序不会被改变,从而保证了有序性。 总结来说,synchronized实现原理是通过Java对象头的Monitor来实现线程同步。它通过检查对象头的Monitor信息来判断是否能够获取对象,同时实现了的可重入性和保证了线程执行的有序性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Javasynchronized实现原理详解](https://download.csdn.net/download/weixin_38556394/12768407)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [synchronized原理](https://blog.csdn.net/xyy1028/article/details/106042888)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值