大家好,这里是淇妙小屋,一个分享技术,分享生活的博主
后续会发布更多MySQL,Redis,并发,JVM,分布式等面试热点知识,以及Java学习路线,面试重点,职业规划,面经等相关博客
转载请标明出处!
任何一个对象都有一个monitor对象与之关联
monitor提供线程阻塞和唤醒机制
当一个monitor被某个线程持有,它将处于锁定状态(获取锁)
ObjectMonitor() {
_header = NULL;
_count = 0; //锁计数器,sychronized是可重入锁
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL; //标识持有锁的线程
_WaitSet = NULL; //等待队列,与Object的wait()和notify()有关
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //同步队列,尝试获取锁的线程会进入EntryList,如果成功获得锁,则设置monitor的_owner为,如果没有成功获得锁,在EntryList中阻塞等待
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
核心字段
synchronized在JVM中通过 monitorenter指令和 monitorexit指令来进入和退出同步代码块
编译器会将monitorenter插入在同步代码块开始的位置,执行该指令时,会尝试获取通同步对象的monitor对象的所有权(尝试获得锁)
编译器会将monitorexit插入在同步代码块的结束处或异常处,释放同步对象的monitor对象的所有权(释放锁)
对象由三个部分组成——对象头,实例数据,对齐填充
对象头结构如下(非数组2个字,数组3个字)
sychronized的实现与 Mark Word有关
线程的栈帧中有一块空间——锁记录
JDK1.6前,sychronzied是重量级锁
JDK1.6做了优化,对锁进行了分类——无锁,偏向锁,轻量级锁,重量级锁
持有偏向锁的线程使用完不会主动释放锁
下一个线程尝试获取偏向锁,会尝试通过CAS将Mark Word中的线程ID替换为自己的,如果原来持有锁的线程已经结束了同步代码块,那么替换就会成功,下一个线程就会取得锁
持有锁的线程使用完后会尝试释放锁,如果释放失败,则升级为重量级锁
会阻塞未获取锁的线程,除了重量级锁,其他情况下,未持有锁的线程不会阻塞
锁 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
偏向锁 | 加锁和解锁不需要额外消耗 | 如果线程间存在锁竞争,撤销锁会带来额外消耗 | 适用于只有一个线程访问同步代码块的情况 |
轻量级锁 | 竞争锁的线程不会阻塞,加快响应速度 | 如果使用竞争不到锁,自旋会消耗CPU性能 | 追求响应速度 |
重量级锁 | 竞争锁的线程阻塞,不消耗CPU | 线程阻塞,响应时间慢 | 追求吞吐量,同步代码快执行时间较长 |
只允许一个线程获得锁后执行同步代码块
JMM不允许同步语句与非同步语句重排序,但是允许同步代码块内在不改变结果的前提下进行重排序(JMM虽然允许同步代码块内重排序,但是在程序员看来仍是有序的)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。