问题解析
Synchronized 在 jdk1.6 版本之前,是通过重量级锁的方式来实现线程之间锁的竞争。
之所以称它为重量级锁,是因为它的底层依赖操作系统的 Mutex Lock 来实现互斥功能。
如下图所示, Mutex 是系统方法,由于权限隔离的关系,应用程序调用系统方法时需要切换到内核态来执行。 这里涉及到用户态向内核态的切换,这个切换会带来性能的损耗。
在 jdk1.6 版本中,Synchronized 增加了锁升级的机制,来平衡数据安全性和性能。 简单来
说,就是线程去访问 Synchronized 同步代码块的时候,Synchronized 根据线程竞争情况,会先尝
试在不加重量级锁的情况下去保证线程安全性。所以引入了偏向锁和轻量级锁的机制。
偏向锁,就是直接把当前锁偏向于某个线程,简单来说就是通过 CAS 修改偏向锁标记, 这种
锁适合同一个线程多次去申请同一个锁资源并且没有其他线程竞争的场景。
轻量级锁也可以称为自旋锁,基于自适应自旋的机制,通过多次自旋重试去竞争锁。自旋锁优点在于它避免避免了用户态到内核态的切换带来的性能开销。
如下图,Synchronized 引入了锁升级的机制之后,如果有线程去竞争锁:
首先,Synchronized 会尝试使用偏向锁的方式去竞争锁资源,如果能够竞争到偏向锁,表示加锁成功直接返回。如果竞争锁失败,说明当前锁已经偏向了其他线程。
需要将锁升级到轻量级锁,在轻量级锁状态下,竞争锁的线程根据自适应自旋次数去尝试抢占锁资源,如果在轻量级锁状态下还是没有竞争到锁, 就只能升级到重量级锁,在重量级锁状态下,没有竞争到锁的线程就会被阻塞,线程状态是 Blocked。
处于锁等待状态的线程需要等待获得锁的线程来触发唤醒。
总结
总的来说, Synchronized 的锁升级的设计思想,本质上是一种性能和安全性的平衡,也就是如何在不加锁的情况下能够保证线程安全性。 这种思想在编程领域比较常见,比如 Mysql 里面的 MVCC 使用版本链的方式来解决多个并行事务的竞争问题。