一、为什么说重量级锁耗费性能资源
- java线程的阻塞或者唤醒是映射到操作系统原生线程之上的,是需要在用户态和核心态之间切换,这种切换会消耗大量的系统资源,因为用户态和内核态都有各自专用的内存空间,专用的寄存器等,用户态切换到内核态需要传递许多变量、参数给内核,内核也要保存好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后可以顺利切换回用户态继续工作(我们自己写的程序代码大部分时间运行在用户态之上,当线程发生阻塞、中断、异常等情况时会切换到核心态来帮助用户态来完成他没有权限执行的操作)。
- 如果线程状态切换是一个高频操作时,这将会消耗很多cpu处理时间;如果对于那些需要同步的简单的代码块,获取锁挂起操作消耗的时间甚至要比用户代码执行的时间还要长,这种同步策略显然时非常糟糕的。synchronized会导致竞争不到锁的线程进入到阻塞状态,所以说它是java语言中一个重量级的同步操作,被称为重量级锁,为了缓解上述性能问题,JVM从1.5开始,引入了轻量级锁与偏向锁,默认启用了自旋锁,他们都属于乐观锁。
二、在Java中有多少种锁类型?什么是对象结构?
- 有四种锁类型:偏向锁、轻量级锁、自旋锁、重量级锁。
- 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、示例数据(Instance)和对其填充(Padding),在学习锁的过程中只需要关注对象头即可。
在上图中,Java的实力分为两类:对象实例、数组实例,无论哪种实例在对象头中都有【markword】和【klass】两部分。
【markword】:用于存储对象自身的运行时数据,如:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等(重点关注)。
【klass】:对象指向它的类元数据的指针,jvm通过这个指针来确定这个对象是哪个类的实例。
- markword 的内部结构,以32位虚拟机为例:
- 加锁流程顺序:偏向锁——>轻量级锁——>自旋锁——>重量级锁
在锁对象状态变化的过程中会对 【markword】中存储的数据进行调整以达到实现不同级别锁的效果。