Monitor
- java对象头
- 普通对象【32bit的jvm】:
- Mark word【32 bits】:对象信息;
- Klass Word【32 bits】:指向Klass对象【Class对象】;
- 数组对象:
- Mark word【32 bits】
- Klass Word【32bits】
- arraylength【32bits】
- Mark word的结构
- normal state:
- hashcode【25bits】+age【3bits,年代区别】+biased_lock【1bit】+ 锁标志【2bits】;
- Biased state:
- thread【23bits】+epoch【2】+biased_lock【1bit】+ 锁标志【2bits】;
- Lightweit Locked state:
- ptr_to_lock_record【30bits】+ 锁标志【2bits】;
- heavyweight Locked state:
- ptr_to_heavyweight_monitor【30bits】+ 锁标志【2bits】
- marked for GC
- 锁标志【2bits】;
- normal state:
- 普通对象【32bit的jvm】:
- Monitor【操作系统提供】工作原理:
- 当使用synchronized时,锁的对象会关联一个Monitor对象;
- monitor对象:
- Owner:只能有一个对象;
- EntryList:阻塞【Blocked】的链表,当锁释放的时候,里面的线程会被唤醒;
- WaitSet:
- 字节码流程:
- 遇到synchronized,将lock对象的Mark word置换位Monitor引用,同时将mark word存储到Monitor中;
- 而且底层有:try-catch-finally机制;
- synchronized的优化:
- 轻量级锁:多个线程错开访问,如果有竞争就会升级;
- synchronized优先轻量级锁;
- 线程内部创建锁记录对象
- 锁记录对象【LockRecord】:
- 【lock record地址,00【轻量级锁标志】】
- 【Object referrence】;
- 将Object referrence指向锁对象,然后使用CAS【乐观锁】的方式替换Object中的mark word的值和【lock record地址,00】
- 锁记录对象【LockRecord】:
- 如果成功了,则表示该线程给对象加锁成功;
- 如果失败了:
- 如果有其他线程已经持有了该对象的轻量级锁,则有竞争,会进入锁膨胀过程;
- 如果是自己持有,那么再添加条Lock record【mark word 00:为null】作为重入的技术;
- 当退出的时候,如果取值有null,则表示有重入,pop出就行;
- 锁记录不为null时,使用CAS将mark word进行恢复;
- 成功:解锁成功;
- 失败:进入重量级解锁流程;
- 线程内部创建锁记录对象
- 锁膨胀:
- 另一个线程加轻量级锁失败后,进入锁膨胀:
- 会给锁对象申请一个monitor锁,并将让Object的mark word指向monitor地址;
- 然乎自己进入monitor的EntryList进行Blocked;
- 当持有轻量级锁退出来后,发现使用CAS给锁对象恢复mark word的时候,会失败;
- 则会根据锁对象的Monitor找到monitor对象,将Ower设置为null;并唤醒EntryList中的线程;
- 自旋优化:
- 进入阻塞会发生上下文切换,降低性能【多核cpu才有意义】;
- 自旋循环几次后重试获取锁【具体自旋次数,比较智能】;
- 偏向锁:
- 轻量级锁在发生锁重入的时候,仍然会要执行CAS操作【进行比较,替换】;
- 在第一次执行CAS操作的时候,将线程ID设置到锁对象的的mark word头里面,之后发现线程ID是自己,表示没竞争,就不用重新执行CAS操作;
- 一个对象创建时:
- 如果开启了偏向锁【默认开启】,那么对象创建后,mark word的最后三位是101,这时候他的thread,epoch,age都是0;
- 偏向锁默认是延迟的,不会再程序启动后立即生效;
- 如果没有开启偏向锁,那么mark word的最后三位是001,他的hashcode,age都是0,只有第一次用到时候,才会赋值;
- 当开启偏向锁,主动使用synchronized的偏向锁后,线程ID的值不会消失,会被持续属于这个对象【仅仅第一次的时候】;
- 偏向锁撤销【禁用】:
- 当开启偏向锁后,在调用hashcode,会直接将该对象的偏向锁撤销【因为没位置存hashcode的值了】。
- 当有其他线程再次使用该偏向锁时,会直接将该对象的偏向锁撤销,然后升级为轻量级锁,之后成正常状态;
- 调用wait/notify时,因为这个只有重量级锁才会有,所以会直接升级为重量级锁;
- 批量重偏向【jvm的优化】:
- 如果对下给你被多个线程访问,但是没有竞争,这时偏向线程1的对象仍然有机会重新偏向线程2;
- 【批量重偏向】:当批量偏向线程1的对象给线程2做锁的时候,会产生偏向锁撤销,当阈值超过20次的时候,jvm会自动优化之后的对象重新偏向至线程2【偏向锁撤销是耗费效率的】;
- 【批量撤销】:当撤销偏向阈值超过40次过后,jvm会认为自己有问题,就不应该偏向,所以整个类的对象都会变为不可偏向,包括新建对象【第40个】;
- 锁消除:
- 当代码被反复执行,JIT就会热点代码优化,当发现对象并不存在多线程的共享,所以就会把锁消除掉,以提升性能;