synchronized锁竞争、锁升级过程、可重入锁

前言:
在我的上篇文章中讲到了,synchronized的实现的底层原理,以及它是如何加锁的。
那么当我们给对象头的markword加锁之后,它是如何一步步的进行锁升级的呢?
我们说到了,锁的四种状态:无锁态,偏向锁,轻量级锁,重量级锁。那么它到底是怎样一步步的升级为重量级锁的呢?

jdk早期的时候,这个synchronized的底层实现是重量级的,重量级到这个synchronized要找操作系统去申请这把锁的地步,这样会造成synchronized的效率非常低,java后来越来越开始处理高并发的时候,感觉这个synchronized太重量级了,
开发出新的框架,改进了之后,才有了锁升级的概念。

一:锁升级过程:

原来呢都是要找操作系统内核申请锁,改进之后,效率变高了。当我们使用synchronized的时候,HotSpot的实现是这样的:

1.上来之后第一个去访问某把锁的线程,来了之后现在这个Object的头上面markword记录这个线程。(如果只有一个线程的话,实际上是没有给这个Object加锁的,在内部实现的时候,只是记录这个线程的ID(偏向锁)),锁状态位为01。

2.当我们的偏向锁有线程争用的时候,就升级为自旋锁(轻量级锁)(原本就是我一个线程加锁(偏向锁),现在又来了一个线程和我抢这把锁),啥时候抢到了这把锁,就将这个锁的id拷贝一份到我自己的线程栈中(给这个线程贴上一个偏向锁线程的id),加锁执行。如果没有抢到锁,就会一直原地进行等待(cas自旋),如果自旋10次,还没有抢到锁,那么就升级为重量级锁。锁状态为00

3.升级为重量级锁(互斥量mutex),重量级锁就是进行了一次用户态到内核态的切换,切换成本高,需要向操作系统内核申请这把锁,不需要消耗资源。线程挂起,进入等待队列,等待cpu调度执行。锁状态为:10。其实是通过内部monitor对象监视器实现的,而monitor是通过操作系统底层Mutex Lock实现的

那么用自旋(cas)好呢,还是synchronied(系统锁)好呢?各有利弊
比如:

   1.当线程数少,执行时间段,就用自旋(cas),不会浪费CPU.        
   2.当线程数量大,执行时间长,就用系统锁(同步锁)。

分享一张图片给大家:更能直观的反应出来:

synchronized锁竞争、锁升级过程、可重入锁_第1张图片
上图就是描述一个锁升级的过程。接下来说说什么是可重入锁:

比方说:一个同步方法调用另一个同步方法,一个方法加锁,另一个方法也得加锁,

加的是同一把锁,同一个线程那这个时候申请仍然会得到该对象的锁。如

synchronized是可重入的,一个方法m1()是同步的,另一个方法m2也是同步的,我

们开始的时候m1()得到了该锁,,然后再m1里调用m2,如果说这个时候不允许任何

线程来获取该锁,就是死锁了。这时候调用m2,发现是同一个线程,因为m2也要申

请这把锁,那么就允许调用。就可以这样理解可重入锁。
synchronized锁竞争、锁升级过程、可重入锁_第2张图片
上图的小例子说明了,同步方法去调用同步方法,然后相当于一个方法加锁,调用另一个加锁的方法,并且锁的是同一个线程。这就是可重入锁。

你可能感兴趣的:(synchronized锁竞争、锁升级过程、可重入锁)