点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。
文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。
每个【锁=锁对象】的对象头由两部分组成:Classpointer+Markword
(1)Classpointer=对象类型指针,JVM就是通过它来确定当前对象是属于哪个class实例
(2)Markword,32位的jvm中Markword是占4B=4byte=32b=32位,存放对象运行时的数据(对象gc时的分代年龄,对象hashcode,当前锁为偏向锁时的线程ID),synchronized锁机制与32位中的最后3位(1+2)密切相关,最后2位是普通锁位,倒数第三位是偏向锁位:
当锁对象被创建出来,此时锁对象为无锁状态,(偏向锁位,普通锁位)=(0,01),所有无锁对象都是可偏向的,即可以被任意线程加偏向锁,当第一个线程对该锁对象加锁时,会把偏向锁位由0变1代表偏向锁生效,同时把线程ID插入锁对象头的Markword中
锁膨胀的过程:无锁(没有线程)->偏向锁(1个线程)->轻量级锁(有第二个及以上的线程竞争锁)->重量级锁(有三个及以上的线程竞争锁)
如何加锁:线程A第一次访问同步代码块中的代码时,先检查当前锁是否可偏向(偏向锁位为0),是则通过CAS获取锁,获取锁之后会在synchronized关键字对应的锁对象的对象头中,在Markword里记录本线程ID,线程A再次访问该同步代码块中的代码时,直接比较锁对象头的Markword的线程ID是否是本线程ID,若是则线程A可重入取锁进而直接访问同步代码块,否则说明是另外一个线程B想访问同步代码块,从而B竞争同一个【锁=锁对象】,此时锁升级
如何锁升级为轻量级锁:当线程B想访问synchronized同步代码块时,会检查synchronized关键字对应的锁对象的对象头中,Markword中线程ID是否为线程B的ID,若不是则B再检查锁对象头中记录的线程A是否还存活,不存活则直接把锁对象先置为无锁状态,再获取锁使其变为偏向锁;若存活则先暂停线程A,撤销偏向锁,再把【锁=锁对象】升级为轻量级锁,然后B线程自旋(不断循环调用cas获取锁,自旋会消耗cpu使得cpu空转,所以自旋有次数限制)
如何升级为重量级锁:B线程在访问同步代码块时发现A线程正在占用锁对象,故把【锁=锁对象】升级为轻量级锁,然后B自旋了,这时又有C线程来,此时轻量级锁升级为重量级锁,同时除了得到锁的那个线程,其他线程均会被阻塞
OK,如果文章哪里有错误或不足,欢迎各位留言。
创作不易,各位的「三连」是二少创作的最大动力!我们下期见!