Week-1-Java-多线程-锁优化轻量级锁偏向锁原理及锁的状态流转
前言学习情况记录 时间:week 1SMART子目标 :Java 多线程记录在学习Java 多线程中 锁优化的有关知识点。 为了进一步改进高效并发,HotSpot虚拟机开发团队在JDK1.6版本上花费了大量精力实现各种锁优化。如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等。(主要指的是synchronized的优化)。 适应性自旋 (自旋锁)为了让线程等待,我们只需要让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。引入自旋锁的原因是互斥同步对性能最大的影响是阻塞的实现,管钱线程和恢复线程的操作都需要转入内核态中完成,给并发带来很大压力。自旋锁让物理机器有一个以上的处理器的时候,能让两个或以上的线程同时并行执行。我们就可以让后面请求锁的那个线程“稍等一下”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只需让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。 自旋锁虽然能避免进入阻塞状态从而减少开销,但是它需要进行忙循环操作占用 CPU 时间,它只适用于共享数据的锁定状态很短的场景。 在 JDK 1.6之前,自旋次数默认是10次,用户可以使用参数-XX:PreBlockSpin来更改。 JDK1.6引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。(这个应该属于试探性的算法)。 锁消除锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行清除。锁清除的主要判定依据来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上数据对待,认为它们是线程私有的,同步枷锁自然就无需进行。 简单来说,Java 中使用同步 来保证数据的安全性,但是对于一些明显不会产生竞争的情况下,Jvm会根据现实执行情况对代码进行锁消除以提高执行效率。 举例说明对于一些看起来没有加锁的代码,其实隐式的加了很多锁,这些也是锁消除优化的对象。例如下面的字符串拼接代码就隐式加了锁: String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,会转化为 StringBuffer 对象的连续 append() 操作: 每个 append() 方法中都有一个同步块。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在 concatString() 方法内部。也就是说,sb 的所有引用永远不会逃逸到 concatString() 方法之外,其他线程无法访问到它,因此可以进行消除。 锁粗化如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗。当多个彼此靠近的同步块可以合并到一起,形成一个同步块的时候,就会进行锁粗化。该方法还有一种变体,可以把多个同步方法合并为一个方法。如果所有方法都用一个锁对象,就可以尝试这种方法。轻量级锁 (@重点知识点)JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态:无锁状态(unlocked)、偏向锁状态(biasble)、轻量级锁状态(lightweight locked)和重量级锁状态(inflated)。 重量级排序 :重量级锁 > 轻量级锁 > 偏向锁 > 无锁 先介绍一下HotSpot 虚拟机对象头的内存布局: 上面这些数据被称为Mark Word - 标记关键词。 其中 tag bits 对应了五个状态,这些状态的含义在右侧的 state 表格中给出。除了 marked for gc 状态(gc标记状态),其它四个状态已经在前面介绍过了。 ...