Synchronized锁的优化详解

Java面试之Synchronized 锁优化(必问)

1. Java对象组成

     Java对象是放在堆内存中的,对象可以分为三个部分,分别是对象头,实例变量和填充字节。
     -对象头 主要包括:1.MarkWord(标记字段) MarkWord 用于存储对象自身的运行时数据(对象的哈希码,分代年龄,当加锁时,这些信息就更具情况被替换为 标记位、线程记录指针,重量级锁指针,线程ID等)。 2.KclassPointer(类型指针)是对象指向它的类元数据的指针。
     -实例变量  存放类的属性数据信息,包括父类的属性信息,这部分内存按4字节对齐。
     -填充数据   由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

2. 偏向锁-》轻量级锁-》重量级锁

2.1为什么引入偏向锁?

    -因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。

2.2偏向锁原理和升级过程

    -当线程1访问代码块并获取锁对象时, 会在对象头和栈帧中记录偏向锁的ID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的ID和Java对象头中的ID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的ID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

2.3 为什么要引入轻量级锁?

    -轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价大,所以可以使用轻量级锁来优化。

2.4 轻量级锁原理和升级重量级锁过程

    -每个线程的栈帧都会包含一个锁记录的结构,内部可以存储锁对象的Mark Word。

    -当线程1获取轻量级锁时会检查对象头的锁标记,若为01,即是无锁状态,把MarkWord复制一份到线程1的栈帧中的锁记录结构,CAS修改MarkWord为线程1锁记录地址,成功加锁,锁标记变为00(轻量锁)。

    -当线程1执行代码块时,线程2也准备获取锁,复制了对象头到线程2的锁记录结构中,当线程2CAS的时候,发现对象头已经被线程1占领,CAS失败,那么线程2就尝试自旋锁等待线程1释放锁。因为自旋所示要消耗CPU的,自旋次数有限制,当自旋次数到了线程1还没有释放锁,或者又有新的线程来竞争锁对象,那么此时CAS就修改Mark为重量级锁(10),重量级锁会把除了拥有锁的线程都阻塞。当线程1执行完成后,会释放锁,唤醒阻塞的线程,竞争锁对象。

3.三种锁的优缺点在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值