JAVA锁升级(锁膨胀)的过程
文章目录
- JAVA锁升级(锁膨胀)的过程
- 1.背景
- 2.概念
- 2.1.无锁
- 2.2.偏向锁
- 2.3.轻量锁(自旋锁)
- 2.4.重量级锁
- 3.锁的状态主要表现在对象头的MarkWord中
1.背景
在jdk获取锁的前期,需要jvm向内核申请,需要计算机内核参与,因此获取锁和释放锁的成本大大提高;在jdk1.6之后有所优化,但是为了提高锁的效率,锁升级还是大大的有必要。锁只能升级不能降级,升级的策略是为了提高锁的效率。
jdk1.6之后锁分四种状态:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
2.概念
2.1.无锁
程序不会有锁的竞争,在程序设计不需要考虑锁的情况下为无锁。
2.2.偏向锁
在程序设计需要考虑锁的情况下,在运行过程中,只有一个线程访问代码块,而且还未产生多线程竞争的条件下,程序会添加一个偏向锁,标识代码块已经有线程访问,当同一个线程再次访问时,可以跳过加锁操作,由于之前没有释放锁,这里也就不需要重新加锁。如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外开销,性能极高。
如果运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。一旦有第二个线程加入锁竞争
,偏向锁就升级为轻量级锁(自旋锁)。升级为轻量级锁的时候需要撤销偏向锁,撤销偏向锁的时候会导致STW(stop the word)
操作。
2.3.轻量锁(自旋锁)
在发生锁竞争的情况下,未获得锁的线程发生自旋,直到获取到锁为止。但是长时间的自旋操作非常影响性能,如果达到了一下两个条件,则会再次升级为重量级锁。
2.4.重量级锁
重量级锁会将自己挂起,加入等待队列,释放cpu资源,等待其他线程唤醒。在JDK1.6之前,synchronized直接加重量级锁,很明显现在得到了很好的优化。
锁 | 优点 | 缺点 | 适用场景 |
---|
偏向锁 | 加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距。 | 如果线程间存在锁竞争,会带来额外的锁撤销的消耗。 | 适用于只有一个线程访问同步块场景。 |
轻量级锁 | 竞争的线程不会阻塞,提高了程序的响应速度。 | 如果始终得不到锁竞争的线程使用自旋会消耗CPU。 | 追求响应时间。同步块执行速度非常快。 |
重量级锁 | 线程竞争不使用自旋,不会消耗CPU。 | 线程阻塞,响应时间缓慢。 | 追求吞吐量。同步块执行速度较长。 |
3.锁的状态主要表现在对象头的MarkWord中
锁状态 | 标志位 |
---|
无锁 | 001 |
偏向锁 | 101 |
轻量锁 | 00 |
重量锁 | 10 |