<JavaEE> 锁进阶 -- synchronized 的锁优化

目录

一、如何形容 synchronized 锁

二、锁升级

2.1 偏向锁

2.2 轻量级锁

2.3 重量级锁

三、锁消除

四、锁粗化


一、如何形容 synchronized 锁

synchronized 锁是一个内部优化非常好的锁,大部分情况下这个锁都是适用的。
在初始阶段 synchronized 是一个乐观锁、轻量级锁、自旋锁,随着锁冲突变得更激烈,synchronized 会转换为悲观锁、重量级锁、挂起等待锁。

与此同时,synchronized 还是一个可重入锁、非公平锁、非读写锁。


二、锁升级

synchronized 的加锁过程
无锁 偏向锁 轻量级锁 重量级锁

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

JVM 将 synchronized 锁分为 以上四个阶段,会根据实际情况,对锁进行升级。应该注意的是,目前所只能升级,不能降级。

2.1 偏向锁

阶段描述:
当锁第一次被一个线程获取时,优先进入偏向锁状态。
偏向锁并非真的“加锁”,而是在对象头中做一个“偏向锁标记”,记录该锁属于哪个线程。
如果后续没有其他线程竞争该锁,那么该锁不会有后续升级操作,减少了加锁带来的系统开销。
如果后续有其他线程竞争该锁,那么锁会真正的加锁,升级为轻量级锁。由于之前已经记录了该锁属于哪个线程,所以此时锁也是被记录的线程获取的。
这种升级操作实际上属于延迟加锁,不必要不加锁,减少了加锁的系统开销,提高了运行效率。

2.2 轻量级锁

阶段描述:
随着锁竞争的开始,锁将进入轻量级锁的状态,在初始状态是通过自旋锁实现的。
自旋锁是循环不断地让线程尝试获取锁。优点在于当锁被释放,其他线程可以第一时间获取到锁。
而自旋锁的缺点也在于会一直占用CPU资源。synchronized 对此也进行了优化,当自旋达到一定的时间或次数时,就不再自旋了,将转换为挂起等待。
同时,synchronized 内部也会统计当前锁对象有多少线程在竞争,如果锁竞争更加激烈,synchronized 就会从轻量级锁升级为重量级锁。

2.3 重量级锁

阶段描述:
重量级锁是指使用内核提供的 mutex 锁。
mutex 锁执行加锁操作时,会先进入内核态,在内核态判定当前锁是否已经被占用。
如果该锁没有被占用,则加锁成功,并切换回用户态。
如果该锁已经被占用,则加锁失败,线程阻塞等待,直到下一次唤醒。

三、锁消除

什么是锁消除?
锁消除是 synchronized 锁的一种较保守的优化策略,通过编译器和JVM判断锁是否可以消除。
这里的锁消除只会处理一些直接可以判断,完全不涉及线程安全问题的锁,比如在单线程环境下使用 StringBuffer 类中的方法。

四、锁粗化

什么是锁粗化?
这里有一个锁的粒度的概念,可以这么认为:在锁对象代码块中的代码越少则认为锁的粒度越细,反之则是越粗。
实际开发中,使用细粒度的锁,往往是为了锁可以被其他线程及时获取。但有时,可能很长一段时间都没用其他线程来竞争这个锁。
因此,如果一段逻辑中出现多次加锁解锁,根据编译器和JVM的判断会自动对锁进行粗化。
锁粗化是指将多个细粒度的锁合并为一个粗粒度的锁,可以在特定场景下提高程序的执行效率,减小系统开销。

阅读指针 -> 《CAS编程及相关类》

链接生成中..........

你可能感兴趣的:(JavaEE,java-ee,多线程)