77 lines
3.4 KiB
Markdown
77 lines
3.4 KiB
Markdown
### 自旋锁与自适应自旋
|
||
|
||
让线程等待一点时间,不放弃处理器执行时间,
|
||
让线程执行一个忙循环,
|
||
锁被占用的时间段,
|
||
可以避免线程切换的开销,但是要占用处理器时间
|
||
|
||
自适应的自旋:
|
||
自旋的时间不再固定,
|
||
而是由前一次在同一个锁上的自旋时间及锁的持有者的状态来确定
|
||
|
||
### 锁消除
|
||
|
||
即时编译器运行时,对检测到不可能存在共享数据竞争的锁进行消除,
|
||
主要判定依据来源于逃逸分析的数据支持
|
||
|
||
### 锁粗化
|
||
|
||
如果有一系列的连续操作都对同一个对象反复加锁和解锁,
|
||
甚至加锁操作是出现在循环体之中的,
|
||
虚拟机探测到有这样一串零碎的操作都对同一个对象加锁,
|
||
会把加锁同步的范围扩展(粗化)到整个操作序列的外部
|
||
|
||
### 轻量级锁
|
||
|
||
"轻量级"是相对于操作系统互斥量来实现的传统锁而言的
|
||
|
||
HotSpot虚拟机头部分为两部分,
|
||
第一部分用于存储对象自身的运行时数据,
|
||
如哈希码、
|
||
GC分代年龄等,
|
||
这部分在对应位数虚拟机中占用32或64各比特,
|
||
称为"Mark World",
|
||
另一部分用于存储指向方法区对象类型数据的指针,
|
||
如果是数组对象,
|
||
还会有一个额外的部分存储数据长度
|
||
|
||
虚拟机在栈帧中建立一个名锁记录的空间,
|
||
用于存储锁对象目前的Mark World的拷贝,
|
||
通过CAS修改将对象的Mark World更新为指向Lock Record的指针,
|
||
这个动作成功了,就代表该线程拥有了这个对象的锁,
|
||
并且更新对象锁标志位为"00",表示该对象处于轻量级锁定
|
||
|
||
如果这个更新操作失败了,意味着至少存在一条线程与当前线程竞争,
|
||
虚拟机先检查对象的Mark World是否指向当前线程栈帧,
|
||
如果是,说明当前线程已获得该对象的锁,
|
||
直接进入同步块执行,
|
||
否则该对象已被其他线程抢占。
|
||
如果过出现两条以上线程争用同一个锁,
|
||
轻量级锁不再有效,膨胀为重量级锁,锁标志状态变为"10",
|
||
Mark World中存储指向重量级锁的指针,后面等待的线程进入阻塞状态
|
||
|
||
解锁操作同样通过CAS进行,
|
||
如果对象的Mark World仍然指向线程的锁记录,
|
||
用CAS操作把对象当前的Mark World和线程中复制的Displaced Mark World替换回来,
|
||
如果能够成功替换,整个同步操作顺利完成,
|
||
替换失败,有其他线程尝试过获取该锁,
|
||
就要在释放锁的同时,唤醒被挂起的线程
|
||
|
||
### 偏向锁
|
||
|
||
目的是消除数据在无竞争的情况下的同步原语,进一步提高程序运行性能,
|
||
偏向锁在无竞争的情况下把整个同步都消除了,CAS都不做了
|
||
|
||
锁对象第一次被对象获取时,
|
||
虚拟机把对象头中的标志位置设为"01",把偏向模式设置为"1",
|
||
表示进入偏向模式,
|
||
同时用CAS操作把获取到这个锁的线程ID记录在对象的Mark World中,
|
||
CAS成功,持有偏向锁的线程每次进入这个锁相关同步块时,
|
||
虚拟机不进行任何同步操作。
|
||
一旦有另一个线程去尝试获取这个锁的状态,偏向模式马上宣告结束,
|
||
后续同步操作按照轻量级锁进行
|
||
|
||
当一个对象计算过一致性哈希码后,就应该一直保持不变
|
||
,就无法进入偏向锁了,
|
||
一个对象处于偏向锁状态,收到计算一致性hash的需求后,
|
||
偏向状态会立即撤销,锁膨胀为重量级锁 |