目录
一、Synchronization原理分析
Synchronization 锁原理是对象同步通过Monitor 来实现的,java每个对象都会有Monitor锁标记,代码块同步使用的是MONITORENTER和MONITOREXIT来同步;类似于操作系统的PV操作,MONITORENTER会执行+1操作,而MONITOREXIT会执行-1操作。
二、Synchronization数据存放格式
2.1、数据存放
Synchronization存放在对象的头部,总共有两种类型,分别是对象和数组类型,不同的类型存放的格式有所差异,普通对象类型暂用3字宽,数组类型占用2字宽,1字宽=4字节=32bit(32位虚拟机),锁只能升级不能降级,如下图,偏向锁升级为轻量级锁之后不能在降级为偏向锁了。
锁状态 | 25bit | 4bit | 1bit | 2bit | |
---|---|---|---|---|---|
23bit | 2bit | 是否是偏向锁 | 锁标志位 | ||
无锁状态 | 对象hashCode | 对象分代年龄 | 0 | 01 | |
轻量级锁 | 指向栈中锁记录的指针 | 00 | |||
重量级锁 | 指向互斥量的指针 | 10 | |||
GC标记 | 空 | ||||
偏向锁 | 线程ID | Epoch | 对象分代年龄 | 1 | 01 |
2.2、偏向锁
从数据结构中可以看出,记录当前线程访问的ID,如果同一个线程访问同一个同步块时,减少加锁和解锁操作,直接使用同步快就可以了,降低了获取锁和加锁的开销。
偏向锁不会主动撤销锁,只有当其他线程竞争资源的时候才会释放锁,变成无锁状态,变成为无锁状态需要当先线程已经执行完了同步块,否则竞争线程会需要等待资源释放。一旦锁资源释放,会唤醒竞争等待的线程。
偏向锁调整相关参数设置,关闭延迟:-XX:BiasedLockingStartUpDelay=0;关闭偏向锁:-XX:UseBiasedLocking=true
2.3、轻量级锁
加锁:(1)在当前线程栈帧空间创建存储对象头MarkWord空间;(2)复制对象MarkWord至(1)创建的空间;(3)使用CAS操作将对象头中的MarkWord替换为指向当前线程所记录的指针,成功表示获取锁,失败表示未获取到锁。
解锁:使用CAS将当前线程的栈帧中保存的MarkWord替换,回到原来的对象头,如果成功,表示当前没有竞争资源失败会自旋重新获取,超过一定阈值之后,会升级为重量级锁,轻量级锁升级为重量级锁之后,没法在降级为轻量级锁了。
2.3、重量级锁
有轻量级锁升级为重量锁,其锁保存了互斥量的指针,并且会阻塞当前线程,当一旦有锁释放之后,会唤醒当前阻塞的线程。
2.4、性能场景比较
锁名称 | 优点 | 缺点 |
---|---|---|
偏向锁 | 单个线程获取锁无需加锁和解锁步骤,降低加锁和解锁开销、使用一个线程多次访问同步 | 多个线程或存在锁竞争,多一个锁撤销开销 |
轻量级锁 | 竞争线程不会阻塞,响应速度快 | 自旋会消耗CPU资源 |
重量级锁 | 吞吐量高 | 线程阻塞,响应时间慢 |