Synchronized 底层实现
Synchronized 底层实现涉及到锁升级的概念。由偏向锁,升级为轻量锁(自旋锁/适应性自旋锁),再升级为重量级锁。
Synchronized 的底层实现与用的是什么版本的 HotSpot 有关,这里以 OpenJdk 1.8 为例。
偏向锁
如果当前 Synchronized 修饰的代码,只有一个线程去访问,那此时线程去加锁/释放锁,显然时没有必要,这只会造成资源浪费。于是引入了偏向锁。
在 Synchronized 加锁的对象的对象头,MarnWord 记录下当前线程的 ID,持有偏向锁的线程,每次进入锁相关同步代码块时,只需要比对一下,MarnWord 记录的线程是否为本线程,如果是线程则获取锁成功。这就是偏向锁,偏向记录线程 ID 的锁。
当线程访问同步块并获取锁时处理流程如下:
- 检查 mark word 的线程 id 。
- 如果为空则设置 CAS 替换当前线程 id。如果替换成功则获取锁成功,如果失败则撤销偏向锁。
- 如果不为空则检查 线程 id为是否为本线程。如果是则获取锁成功,如果失败则撤销偏向锁。
如果发生线程竞争,2、3失败的情况,那么,会升级为轻量锁(自旋锁)。
轻量锁
轻量锁指的时锁的状态,而自旋是一种获取锁的机制。自旋是为了让当前线程“稍等一下”,适用于同步代码块执行时间短的情况。
自旋锁
当线程无法获取锁时,该线程进入循环等待(自旋),此时该线程状态不是阻塞的,也就无需耗费资源去唤醒线程。默认情况下,固定 10 次自旋次数,可以使用参数 -XX : PreBlockSpin
更改。
适应性自旋锁
在 JDK1.6 引入了适应性自旋锁,自旋次数不在固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果对于某个锁很少自旋成功那么以后有可能省略掉自旋过程以避免资源浪费。
如果线程始终无法获取到锁,那么会升级为重量级锁。
重量级锁
重量级锁是将除了拥有锁的线程以外的线程都阻塞。
进入到重量锁,涉及到操作系统调度,会产生额外的开销。这部分内容涉及到操作系统方面的知识,我不是很懂,暂且忽略。
总结
- 偏向锁通过对比 Mark Word thread id 解决加锁问题。
- 轻量级锁是通过用 CAS 操作 Mark Word 和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。
- 重量级锁是将除了拥有锁的线程以外的线程都阻塞。
参考链接:
锁升级概念:没错,我就是厕所所长!(一)
锁升级概念:没错,我就是厕所所长!(二)
synchronized 实现原理