https://www.cnblogs.com/dsj2016/p/5714921.htmlhtml
https://www.zhihu.com/question/55075763程序员
优化包括自旋锁、锁粗化、锁消除(锁清除)、轻量级锁和偏向锁。安全
这些都是Java虚拟机层面的优化,对Java程序员是无感知的(Java程序员在程序层面是看不出什么的,可是JVM内部已对synchronized作了优化)。要说有感知的话,就是synchronized在多线程下的性能提升了(JDK1.6相比于JDK1.5),这些都得益于JVM层面锁的优化,就是上面所说的自旋锁、锁粗化、锁清除、偏向锁和轻量级锁。多线程
一般状况下,共享数据的锁定状态只持续很短的一段时间,为了这很短的一段时间进行上下文切换并不值得。性能
自旋就是不停循环看是否能等到上个线程本身释放锁。这个问题是基于一个现实考量的:不少拿了锁的线程会很快释放锁。由于通常敏感的操做不会不少。固然这个是一个不能彻底肯定的状况,只能说整体上是一种优化。优化
举个例子就比如一我的要上厕所发现厕所里面有人,他能够:1,等一小会。2,跑去另外的地方上厕所。等一小会不必定能等到前一我的出来,不过若是跑去别的厕所的花费的时间确定比等一小会结果前一我的出来了长。固然等完告终果那我的没出来仍是要跑去别的地方上厕所这是最慢的。spa
自适应自旋能够根据以往自旋等待时间的经验,计算出一个较为合理的本次自旋等待时间。操作系统
好比第一次设置最多自旋10次,结果在自旋的过程当中成功得到了锁,那么下一次就能够设置成最多自旋20次。道理是:一个锁若是可以在自旋的过程当中被释放说明颇有可能下一次也会发生这种事。那么就更要给这个锁某种“便利”方便其不阻塞得锁(毕竟快了不少)。一样若是屡次尝试的结果是彻底不能自旋等到其释放锁,那么就说明颇有可能这个临界区里面的操做比较耗时间。就减少自旋的次数,由于其可能性过小了。线程
如有一系列操做,反复地对同一把锁进行上锁和解锁操做,编译器会扩大这部分代码的同步块的边界,从而只使用一次上锁和解锁操做。设计
试想有一个循环,循环里面是一些敏感操做,有的人就在循环里面写上了synchronized关键字。这样确实没错不过效率也许会很低,由于其频繁地拿锁释放锁。要知道锁的取得(假如只考虑重量级MutexLock)是须要操做系统调用的,从用户态进入内核态,开销很大。因而针对这种状况也许虚拟机发现了以后会适当扩大加锁的范围(因此叫锁粗化)以免频繁的拿锁释放锁的过程。
编译器会清除一些使用了同步,但同步块中没有涉及共享数据的锁,从而减小多余的同步。
经过逃逸分析发现其实根本就没有别的线程产生竞争的可能(别的线程没有临界量的引用),而“自做多情”地给本身加上了锁。有可能虚拟机会直接去掉这个锁。
使用CAS取代互斥同步。
重量级锁是一种悲观锁,使用互斥同步来保证线程的安全;
轻量级锁是一种乐观锁,使用CAS操做进行同步。
当线程请求锁时,若该锁对象的Mark Word中标志位为01(未锁定状态),则在该线程的栈帧中建立一块名为『锁记录』的空间,而后将锁对象的Mark Word拷贝至该空间;而后经过CAS操做将锁对象的Mark Word指向该锁记录;
若CAS操做成功,则轻量级锁的上锁过程成功;
若CAS操做失败,再判断当前线程是否已经持有了该轻量级锁;若已经持有,则直接进入同步块;若还没有持有,则表示该锁已经被其余线程占用,此时轻量级锁就要膨胀成重量级锁。
轻量级锁比重量级锁性能更高的前提是,在轻量级锁被占用的整个同步周期内,不存在其余线程的竞争。若在该过程当中一旦有其余线程竞争,那么就会膨胀成重量级锁,从而除了使用互斥量之外,还额外发生了CAS操做,此时耗时更长,会更慢。
偏向锁是为了消除无竞争状况下的同步原语,进一步提高程序性能。
轻量级锁是在无竞争的状况下使用CAS操做来代替互斥量的使用,从而实现同步;而偏向锁是在无竞争的状况下彻底取消同步。
它们都是乐观锁,都认为同步期间不会有其余线程竞争锁。
当线程请求到锁对象后,将锁对象的状态标志位改成01,即偏向模式。而后使用CAS操做将线程的ID记录在锁对象的Mark Word中。之后该线程能够直接进入同步块,连CAS操做都不须要。可是,一旦有第二条线程须要竞争锁,那么偏向模式当即结束,进入轻量级锁的状态。
偏向锁能够提升有同步但没有竞争的程序性能。可是若是锁对象时常被多条线程竞争,那偏向锁就是多余的。