JAVA并发-sychronized优化

接着上一篇: JAVA并发-sychronized底层实现
既然学习了一波原理,也晓得sychronized后续是被优化过的,所以今天就研究下sychronized优化机制,学习是一个循序渐进的过程,让我们出发吧!!!

sychronized优化前是怎样的?
1.我们知道sychronized锁操作底层是基于对Monitor的操作实现的,Monitor底层又是基于Mutex Lock即互斥锁
2.互斥锁的加锁指令时间较久,所以基于互斥锁实现的加锁机制被称为重量级锁
3.获取锁失败后,线程进入阻塞状态,线程切换会带来较大的性能损耗,因为操作系统在进行线程切换时,需要从用户态变为核心态,这个过程很耗时

如何进行优化的?
1.Jdk1.6引入了偏向锁和轻量级锁,减少底层对互斥锁的申请次数
2.自旋锁的使用,以及Jdk1.6之后引入的自适应自选锁,间接地减少线程切换的次数
3.jvm内部使用锁消除机制,避免了底层对互斥锁的申请
4.采用锁粗化方式,减少连续的加锁、解锁操作
5.锁消除,直接消除锁,线程执行时不需要再请求锁

sychronized锁升级过程是怎样的?
1.无锁状态->偏向锁->轻量级锁->自旋锁->重量级锁
2.锁只能升级,不能降级

什么是无锁状态?
1.没有任何线程来获取锁
2.对象头的Mark Word中偏向锁标志位为0,处于无锁状态,锁标志位"01"

什么是偏向锁?
1.资源消耗最低的一种锁,在单线程情况下使用的就是偏向锁
加锁过程:
1.对象头Mark Word处于无锁状态,这时单线程获取锁,Mark Word锁标志位置为"01"
2.通过CAS方式修改Mark Word中偏向线程ID为该单线程id,偏向锁状态置为1
3.后面每个线程获取锁时,判断当前获取锁的线程id是否和Mark Word中偏向线程id一致,如果一致,则不需再进行任何操作,如果不一致,则会升级到轻量级锁

如何释放偏向锁?
1.线程不会主动去释放偏向锁,需要等待全局安全点(没有字节码在执行)
2.首先暂停拥有偏向锁的线程,即stop-the-world
3.判断锁对象是否处于锁状态,撤销偏向锁后,对象头Mark Word锁字段变为无锁状态或轻量级锁

偏向锁的适用场景?
1.适用于只有一个线程在执行同步代码
2.因为释放偏向锁会引起stop-the-world,所以在竞争较多的情况下要禁用偏向锁

如何配置偏向锁的开启与关闭?
1.开启:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
2.关闭:-XX:-UseBiasedLocking

什么是轻量级锁?
资源消耗比偏向锁稍高的一种锁,是在无竞争环境下使用的锁
加锁过程:
1. 一个线程尝试获取锁,判断对象头Mark Word中是偏向锁状态,而且线程id不一致,这时就会发生锁升级
2.jvm在当前线程的栈帧中建立一个Lock Record的空间,用于存储对象的Mark Word拷贝,官方称为Displaced Mark Word
3.拷贝对象Mark Word成功后,通过CAS方式修改原对象Mark Word中存储偏向线程id那块区域,修改为指向线程栈中Lock Record的指针
4.通过CAS方式修改Lock Record中owner字段指向原对象Mark Word
5.CAS操作成功后,将原对象Mark Word中锁标识字段置为"00",锁状态变为轻量级锁
6.如果在第1步时,发现对象头Mark Word中是轻量级锁状态,则通过CAS方式指向3-5步操作,如果CAS成功,说明不存在资源竞争,当前依然是轻量级锁,如果CAS失败,说明存在竞争,则会尝试进行自旋获取锁

什么是自旋锁?
1.线程通过CAS方式获取轻量级锁资源失败后,不会立刻进入阻塞状态,会执行特定次数的循环去尝试获取锁资源
2.线程执行特定次数的循环逻辑被称为自旋

什么是自适应自旋锁?
1.jdk1.6引入自适应自旋锁
2.自旋次数由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定
3.如何自旋很少成功,那么会省掉自旋的步骤

如何配置自旋锁的开启与关闭?
1.开启:-XX:+UseSpinning
2.关闭:-XX:-UseSpinning
3.自旋次数:-XX:-PreBlockSpin,自适应自旋次数是不固定的
3.jdk1.7之后,不需要设置该参数,jvm自动控制

什么是重量级锁?
1.其实在前面也提到了sychronized优化前只有重量级锁,也就是基于互斥锁,其性能是比较低的
加锁过程:
1.通过自旋仍旧获取锁失败之后,线程进入阻塞状态,后面申请锁就会走Monitor锁操作的逻辑
2.具体Monitor的操作可以参考前面写的一篇文章: JAVA并发-sychronized底层实现

什么是锁粗化?
1.首先我们要了解,锁的粒度不能无限拆分,否则反而会降低性能,试想在一个循环内部加锁每次都要进入临界区,无疑会非常耗性能,正常来说,锁拆分粒度不要超过cpu核心数
2.当我们面临的锁粒度非常小时,而且这会影响性能时,我们要考虑将锁的范围扩大,这就是锁的粗化

什么是锁消除?
1.jvm在编译执行(JIT)时,通过对运行上下文进行扫描,去除不可能存在竞争的锁
2.在方法中声明的局部变量,如果调用该变量的同步方法,JIT就会进行锁消除,例如:StringBuffer.append()方法的使用





你可能感兴趣的:(java并发)