从零开始java多线程到分布式锁(九):Synchronized的优化

目录

一:Synchronized的底层优化

二:锁状态

1.锁状态

2.锁在对象的保存

3.锁优化的基本原理(很重啊哟)

三:轻量级锁

1.轻锁的加锁过程:

2.轻锁的解锁过程

四:偏向锁 

1.偏锁获取锁的过程

2.偏锁的释放过程 

五:锁状态的相互转换

六:锁的其他优化

1.适应性自旋

2.锁粗化

3.锁消除

七:总结

1.优化锁的优缺点以及关闭锁优化

2.轻锁/偏锁都是基于Synchronized实现的,本质而言都是要切换到JVM的和状态的。


一:Synchronized的底层优化

  在上面一篇中我们说到Synchronized锁在运行同步方法会在应用层切换到JVM核状态,很多时候代码的运行时间还会小于状态切换,因此其实一个重量级的锁。但是我们在代码里很多时候还是会看到这种锁,应为其用法足够简单足够方便,在一些性能能需求不是太重要的系统中,这种使用是完全没有问题的。

   在jdk1.6之后,jdk对于这种所做了一些优化,让其足够简单也能足够效率。在jdk1.8包括之后,默认是开启sysnchronized的锁优化的(当然,也可以禁用锁优化,至于为何禁用,如何禁用我们将在文章的后部分说明), 因此这里我们就只需要了解一下其如何进行优化的。

  (注意:为了方便下文中的所有的锁都指的的是Sysnchronized,除非特指说明)

 

二:锁状态

1.锁状态

  jdk为了区分出锁的优化概念提出了4中锁状态:无锁,轻量级锁,偏向锁以及重量级锁。其中锁升级的顺序为:偏向锁 -》轻量级锁 - 》重量级锁,并且锁稚嫩那个升级不能降级。也就是说默认开启锁优化的时候默认为轻量级锁,如果出现锁竞争等情况锁会依次升级。

   从上面的一段话中我们可以看到,一个方法或者一个对象方法同时只能持有一种锁状态。

2.锁在对象的保存

  我们知道重锁Synchronized是依赖于JVM的Monitor的,也就是说依赖于对象的Monitor的。那么锁是如何保存在对象中的呢?下面我们就以32位jdk画图(图表来自网络):

从零开始java多线程到分布式锁(九):Synchronized的优化_第1张图片

3.锁优化的基本原理(很重啊哟)

  轻锁的优化:轻量级锁的优化,是基于使用了Sysnchronized的情况下但是方法却没有出现多线程竞争而进行的性能消耗的优                                化。

  偏锁的优化:轻锁向偏锁的优化,是基于轻锁的存在下,减少不必要的轻锁执行路径。

 

三:轻量级锁

  轻量级锁是在没有出现多线程竞争的情况下默认是使用的,但是一旦出现并发情况下轻锁会自动膨胀为重锁,当然这种情况是不需要我们手动操作的。

1.轻锁的加锁过程:

  总结一下三种加锁结果:(1)轻锁加锁成功,执行同步方法。

                                              (2)当前线程拥有该锁,执行同步方法。

                                              (3)多线程竞争,轻锁膨胀为重锁,其他线程阻赛

从零开始java多线程到分布式锁(九):Synchronized的优化_第2张图片

2.轻锁的解锁过程

      总结一下2种结果:(1)没有多线程竞争,只需同步即可。

                                        (2)多线程竞争,当前线程释放锁,并唤醒其他的线程

从零开始java多线程到分布式锁(九):Synchronized的优化_第3张图片

 

四:偏向锁 

    引入偏向锁是为了优化轻锁的加锁/解锁的执行路径,在上面的轻锁的加锁/解锁的过程中我们可以看到多次使用可CAS的操作(CAS操作是一种消耗资源的操作,不清楚的可以自行百度)。下面看一下偏锁的获取锁/释放锁过程;

1.偏锁获取锁的过程

  偏锁的获取其实是基于轻锁的实现,也就是这里我们说偏锁是获取而不是加锁的原因。

  总结2种结果:(1)没有多线程的锁竞争,偏锁加载完成,执行同步代码。

                            (2)存在多线程的锁竞争,当前线程挂起,其他被堵塞的线程继续执行。

从零开始java多线程到分布式锁(九):Synchronized的优化_第4张图片

 

2.偏锁的释放过程 

  偏向锁的释放其实是在达到全局安全点时,自动释放。

 

五:锁状态的相互转换

  .其实,如果看懂上面的轻锁/偏锁的加锁/解锁转换。其实就很明确,就是存在多线程竞争状态。下面特出一幅图:

从零开始java多线程到分布式锁(九):Synchronized的优化_第5张图片

六:锁的其他优化

1.适应性自旋

       无论是哪种锁,都存在着一个获取锁的过程,锁获取一般都是通过CAS的操作获取的。如果有其他线程占据锁并且没有释放,那么当前线程会不停的循环去获取锁,这种就叫做锁的自旋。锁的自旋是十分消耗系统资源的(毕竟Synchronized的获取都是要依赖JVM内核的Monitor的)。因此,我们可以手动的去设置锁的自旋次数。  可以设置 -XX:UseSpining开启自旋锁(在jdk1.6以及之后都是默认开启的),-xx:PreBlockSpin设置自旋次数(当然一般不建议,jdk1.6之后添加自适应自旋)

2.锁粗化

 这个就比较容易理解了,如果有一个方法调用了多个加锁的方法,那么完全可以将多个锁的方法去掉,并且在该方法上加锁。

3.锁消除

锁消除即删除不必要的加锁操作。根据代码逃逸技术,如果判断到一段代码中,堆上的数据不会逃逸出当前线程,那么可以认为这段代码是线程安全的。

 

七:总结

1.优化锁的优缺点以及关闭锁优化

   jdk自身对于Synchronized的锁优化是为了便于我们简单而又高性能的使用同步锁,但是其具有普遍性适用于大部分的场景。但是这2个优化锁都是基于同步方法没有多线程竞争的情况下的优化,一旦存在多线程竞争反而会将降低效率,原因是存在一个锁升级的过程。如果我们代码方法确定一定会存在多线程并发竞争的情况,那么建议关闭锁优化,关闭方法:-XX:-UseBiasedLocking。

2.轻锁/偏锁都是基于Synchronized实现的,本质而言都是要切换到JVM的和状态的。

你可能感兴趣的:(Synchronized的优化)