锁优化
JDK5-6的重要升级 适应性自旋 锁消除 锁粗化 轻量级锁 偏向锁 更高效地解决竞争问题
自旋锁与自适应自旋
互斥同步最大的问题就是上下文切换
共享数据锁定只会持续很短的时间 为了这段时间挂机或恢复线程很不值得
如果不止一个核心,不如让后面的线程等一会 不放弃处理器的执行时间
为了等待 所以我们让线程执行一个忙循环(自旋) while循环
自旋时间默认 10 如果自旋时间太长 也会白白消耗处理资源 浪费性能
自适应自旋:自旋时间不是固定的 自旋时间由上一个自旋时间和拥有者决定 如果很少成功获得锁,就会省略自旋过程 如果经常成功 就会试图更长地自旋
锁消除
就是在即时编译器运行时,对不可能存在竞争的锁进行消除
如果数据不会逃逸出去被其他线程访问 就可以当成栈上数据 经典栈上分配 就不用加锁
字符串加法JDK5之前 会转为StringBuffer JDK6之后是StringBuilder
sb对象会在append的时候加锁 但是sb不会逃逸出去 就需要锁消除
锁粗化
如果一系列连续操作都需要反复加锁和解锁,即使没有线程竞争,频繁的互斥同步操作也会导致不必要的性能损耗
锁粗化:对于零碎的锁 如果检测到,就要扩大锁的范围 只要加锁一次就好
轻量级锁
JDK轻量级锁,相对sync重量级锁来说非常轻
轻:不是代替重 只是在没有多线程竞争下 减少重量级锁的性能损耗
轻量级锁 要考虑偏向锁的原理和运作过程
**要对HotSpot内存布局有了解 **
HotSpot对象头 两部分 第一部分(运行时数据) 哈希码 GC年龄等 在JVM占用32或64字节 称为“Mark Word”
另一部分 存储指向方法区对象类型数据的指针
Mark Word是一个非固定的动态数据结构
轻量级锁工作流程 代码快进入同步块的时候,如果同步对象未被锁定(锁标志01),JVM在当前线程栈中创建一个 **锁记录(Lock Record) **的空间 存储 Mark Word
JVM用CAS操作 尝试把对象的Mark Word更新为指向Lock Record指针
如果成功 就代表拥有了这个对象的锁 修改锁标志位 00 表示轻量级锁定状态
如果更新失败了,就说明有线程竞争 先判断Mark Word是否指向当前栈帧
是说明已经有锁直接进入同步块就好 没有就说明抢占 要用重量级锁
锁标志10
解锁也是用CAS操作 如果对象的Mark Word仍指向线程的锁记录 就把复制的换回来就好
如果有竞争在释放锁的同时,唤醒被挂起的线程
CAS依据:**对于绝大部分锁,在同步周期内不存在竞争 **这一经验法则
优势:成功避免频繁使用互斥量开销
劣势:如果确实存在锁竞争 除了锁开销还增加了CAS开销
偏向锁
目的:消除数据在无竞争下的同步原语
CAS:消去同步使用的互斥量
偏向锁:就是消去整个同步 CAS都不用了
偏:锁会偏向于第一个获得它的线程 如果接下来锁一直没被别人获取,就永远不用同步
锁第一次被线程获取的时候 对象头标志设置为01 偏向模式 1
CAS操作把获取到锁的线程ID记录在Mark Word
CAS成功 每次持有偏向锁的线程进入相关同步块 可以不进行同步操作
一旦出现另外一个线程尝试获取锁,偏向模式马上结束 按照轻量级锁去执行
哈希码的位置被占问题:如果一个对象计算过哈希码之后,就不会进入偏向状态 如果同步需要计算哈希码,会撤销偏向状态改为重量级锁
缺点:对经常有线程竞争的程序效果不好 反而增加开销
参考资料
《深入理解Java虚拟机:JVM高级特性与最佳实践》第三版 -周志明
《Java并发编程的艺术》 -方腾飞