【JavaEE多线程】synchronized原理篇

目录

一、认识对象头 

 32位JVM的Mark Word的默认存储结构

一、synchronized的优化机制

1)无锁状态

2)偏向锁状态:非必要,不加锁

 3)轻量级锁

4)重量级锁:挂起等待

二、锁消除

三、锁粗化

锁的粒度 

锁粗化的好处


      在这一篇文章当中,我们也提到了synchronized的作用。

Java对于synchronized的初步认识_革凡成圣211的博客-CSDN博客synchronized,死锁,https://blog.csdn.net/weixin_56738054/article/details/128062475?spm=1001.2014.3001.5501       回顾一下,在synchronized当中,两个线程如果针对同一个对象加锁,如果一个线程可以获取到锁,另外一个线程就会进入阻塞等待的状态。

       关于synchronized锁的一些特性,在这一篇文章当中,已经提到了。
【JavaEE进阶】锁的特性_革凡成圣211的博客-CSDN博客Java锁的特性https://blog.csdn.net/weixin_56738054/article/details/128574608?spm=1001.2014.3001.5501

       下面,再回顾一下synchronized的几个特性:

        特性一、synchronized是一个非公平锁;

        特性二、synchronized是一个可重入锁;

        特性三、synchronized是一个悲观锁;

        特性四、synchronized是一个互斥锁,两个线程不可以同时占有一把锁;

        特性五、synchronized可以由一个轻量级锁转化为重量级锁


一、认识对象头 

       synchronized用的锁是存在Java对象头当中的。如果对象是数组类型,则虚拟机用3个字宽存储对象头。如果对象是非数组类型,则用2字节宽存储对象头。

对象类型 存储大小
数组类型 3字宽(1字宽等于4字节)
非数组类型 2字宽

 32位JVM的Mark Word的默认存储结构

锁状态 25bit 4bit 1bit是否偏向锁 2bit锁标志位
无锁 对象的hashCode 对象分代年龄 0 01

         在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。


一、synchronized的优化机制

       以下四个过程:就是在线程进入synchronized代码块当中之后,加锁的过程,就可能会经历下面这几个阶段。        

synchronized(locker){

  //在这个内部,进行加锁的过程

}

synchronized内部其实还有一些优化机制,存在的目的就是为了让这个加锁的过程更加高效。

下面,将重点分析一下加锁的几个过程:

1)无锁状态

 不加锁,这种状态一般不会存在。如果真的存在,那么很有可能是被编译器把锁给优化掉了。


2)偏向锁状态:非必要,不加锁

       当进入synchronized代码块当中之后,首先会进入到偏向锁的状态;

       其实偏向锁,就是一个线程对对象尝试加锁的一种状态,并没有真正施加锁,而是先对于这个对象的对象头当中做一个标记。做标记这个过程,其实相比于真正加锁,还是轻量了不少的。

       如果整个使用锁的过程当中,都没有出现锁竞争,那么这个标记,就会在线程离开synchronized之后释放;

       如果另外一个线程同时也尝试对于同一个对象加锁,那么就会造成锁升级,转变为轻量级锁。

   【JavaEE多线程】synchronized原理篇_第1张图片 


 3)轻量级锁

   对于synchronized,它的轻量级锁,是通过自旋锁的方式来实现的。

   对于自旋锁的解释,也已经在这一篇文章当中提到了:
【JavaEE进阶】锁的特性_革凡成圣211的博客-CSDN博客Java锁的特性https://blog.csdn.net/weixin_56738054/article/details/128574608?spm=1001.2014.3001.5501

       自旋锁虽然不会造成线程的阻塞等待,但是如果无法通过CAS获取到锁,就会一直在循环当中尝试获取锁。

       如果获取到锁的线程很快就释放锁了,那么也就意味着自旋是划算的。

       如果获取到锁的线程一直没有释放锁,那么这个自旋的过程是很消耗cpu资源的

       因此,当synchronized处于自旋锁的状态的时候,它的内部会有一个计数器,当计算的数量达到一定的数目之后,就停止自旋,升级为重量级锁


4)重量级锁:挂起等待

       重量级锁,会造成线程阻塞等待。这个过程,则是基于操作系统原生的API来实现的。

       这个时候,如果线程进行了重量级锁的加锁过程,那么获取不到锁的线程就会被操作系统调度离开CPU内核,被放入阻塞队列当,暂时不参与CPU的运算调度。

       重量级锁,因为涉及线程调度离开CPU,调度回到CPU的过程,相比起轻量级锁,会更

加消耗CPU的资源。

         目前JVM,只支持锁升级的操作,不支持锁降级的操作。 


二、锁消除

锁消除是发生在编译阶段的事件 

编译器的智能判定,看当前代码是否真的需要加锁。

如果不需要加锁 ,代码当中也加锁了,那么这个锁就会被编译器消除。


经典现象:对于StringBuffer,如果单线程环境使用,那么编译器就会把这个锁消除掉。


三、锁粗化

锁的粒度 

对于synchronized:它所包含的代码越多,粒度就越粗。包含的代码越少,粒度就越细。

在保证线程安全的情况下面,锁的粒度越细越好 

对于ConcurrentHashMap来说,它的put方法的粒度就比Hashtable的put方法的粒度细很多。


锁粗化的好处

       如果对于一些应用场景,两次加锁之间,间隙非常小。

       但是由于加锁、解锁的过程也是需要一定的开销的。那不如直接使用一把大锁搞定,就不再反复加锁、解锁了。

       这种变多把小锁一把大锁的现象,就被称为锁的粗化

【JavaEE多线程】synchronized原理篇_第2张图片


你可能感兴趣的:(java,开发语言)