【JavaEE多线程】synchronized原理篇

在这一篇文章当中,我们也提到了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的默认存储结构

锁状态25bit4bit1bit是否偏向锁2bit锁标志位

无锁对象的hashCode对象分代年龄001

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

一、synchronized的优化机制

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

synchronized(locker){

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

}

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

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

1)无锁状态

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

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

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

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

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

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

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方法的粒度细很多。

锁粗化的好处

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

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

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

————————————————

版权声明:本文为CSDN博主「革凡成圣211」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_56738054/article/details/128826633

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值