JDK版本: 1.6
前言
jdk1.6对synchronized锁进行了一些列的优化, 由原来的直接使用重量级锁,改为锁强度逐渐升级的方式,主要分为三种强度的锁,无锁>偏向锁>轻量级锁>重量级锁,且只能升级不能降级. 期间使用了CAS和自旋,自适应自旋,锁粗化,锁消除等技术
基本概念
自旋锁
根据经验, 线程自旋等待一会, 不放弃CPU执行时间, 通常可以取得很好的效果, 一般都能拿到锁
自适应自旋
根据上次自旋的效果, 如果上次获取锁成功, 那么本次自旋的次数会增加, 如果之前是失败的, 那么自旋次数会一直减少直到减少到0,避免浪费处理器资源
锁消除
StringBuffer的append方法如果在一个方法块中运行, 虚拟机检测到方法不会逃逸出去, 那么就会进行锁消除
锁粗化
单线程一个代码块中如果频繁的加锁和释放锁, 对性能会有影响, 于是通过锁粗化, 在开始和结束的时候进行加锁和释放即可
无锁
锁资源未被任何线程持有
偏向锁
大多数情况下, 锁不存在多线程竞争的情况, 都是单个线程持有,为了避免频繁使用CAS更新对象头部的MarkWord,于是引入了偏向锁, 线程只需要判断对象头部的线程Id是否为当前线程,如果是,即可获取到锁资源
轻量级锁
轻量级锁的引入,处于一种假设,假设多线程竞争锁资源的情况下,线程可以通过自旋一段时间,即可获取到锁资源.
这样的好处是申请锁的线程不必放弃CPU使用时间,降低线程上下文切换所造成的开销
重量级锁
直接阻塞等待锁资源的线程,通过操作系统mutex机制,锁定资源,涉及到内核线程和用户线程的切换,开销相对较大
锁升级过程
无锁>偏向锁
需要注意的是,偏向锁的撤销,是被动的,只有其他线程尝试获取偏向锁,才会去撤销当前线程所持有的偏向锁,然后根据当前线程的方法执行情况,设置对象头中的锁状态为无锁状态或者升级为偏向锁
偏向锁>轻量级锁
- 线程2来竞争锁对象;
- 判断当前对象头是否是偏向锁;
- 判断拥有偏向锁的线程1是否还存在;
- 线程1不存在,直接设置偏向锁标识为0(线程1执行完毕后,不会主动去释放偏向锁);
- 使用cas替换偏向锁线程ID为线程2,锁不升级,仍为偏向锁;
- 线程1仍然存在,暂停线程1;
- 设置锁标志位为00(变为轻量级锁),偏向锁为0;
- 从线程1的空闲monitor record中读取一条,放至线程1的当前monitor record中;
- 更新mark word,将mark word指向线程1中monitor record的指针;
- 继续执行线程1的代码;
- 锁升级为轻量级锁;
- 线程2自旋来获取锁对象;
轻量级锁>重量级锁
锁升级全过程
首先看下内存中和synchronized锁相关的数据结构,主要包括:线程,方法栈,对象头部,MonitorObject四个对象
- 对象头信息被称为mark word,保存了锁相关的信息
- MonitorObject用于实现重量级锁, 底层通过操作系统的mutex来阻塞获取锁资源的线程
主要过程
1.初始状态为无锁状态
2.线程1判断java对象头部中的线程Id是否为当前线程
如果是: 则获取锁成功, 执行步骤13
如果不是: 则CAS修改**对象头部**的中的线程Id为**线程1**的线程Id,获取偏向锁成功
3.线程1继续执行
4.线程2尝试获取锁,发现对象头部中的线程Id不是线程2的线程Id,于是CAS尝试修改对象头部线程Id,由于此时头部中的线程Id不为null, 修改失败
5.执行撤销线程1偏向锁操作
6.stop the world
7.检查线程1的栈帧列表中是否存在该锁信息,用于判断被加锁的方法是否正在运行
如果没有运行,则锁状态被设置为**无锁转态**,**线程2**使用CAS获取偏向锁成功,执行步骤13
如果正在运行,即锁定的代码块正在**方法栈列表**中执行, 那么锁升级为轻量级锁,此时方法栈中会生成LockRecord信息,并使用CAS替换对象头的MarkWord信息
8.线程1被唤醒继续运行
9.线程2通过CAS自旋尝试获取轻量锁
**线程1**执行完毕,释放轻量级锁, 于是**线程2**获取轻量级锁成功,执行步骤13
**线程1**还在执行,则**线程2**获取锁失败,锁膨胀,升级为**重量级锁**,**线程2**阻塞
10.线程1继续执行
11.线程1执行完毕后,释放锁,并唤醒阻塞中的线程,此时线程2被唤醒
12.此时如果只剩下线程2尝试去获取锁资源,那么线程2获取重量级锁成功,执行步骤13
13.成功返回
锁的优缺点比较
图片来自书籍: java并发编程的艺术
结语
对于锁升级全过程这部分的内容, 大家有任何想法,欢迎评论区留言~
参考资料
书籍:java并发编程的艺术
ObjectMonitor数据结构: http://www.sohu.com/a/328600103_120210224>
ObjectMonitor英文资料: https://www.artima.com/insidejvm/ed2/threadsynchP.html
Object类中的wait和notify方法: https://blog.csdn.net/qq_38293564/article/details/80432875
https://blog.csdn.net/qq_38293564/article/details/80409861
https://blog.csdn.net/u012465296/article/details/53022317
https://www.cnblogs.com/javaminer/p/3889023.html
https://blog.csdn.net/tongdanping/article/details/79647337
https://blog.csdn.net/zqz_zqz/article/details/70233767
https://www.cnblogs.com/paddix/p/5405678.html
https://www.jianshu.com/p/5da9ef598604
http://www.voidcn.com/article/p-hagongck-wk.html
https://juejin.im/post/5abc9de851882555770c8c72
https://blog.csdn.net/wojiao228925661/article/details/100145157