探“锁”源头:synchronized、偏向锁与锁膨胀的机密!

打印 上一主题 下一主题

主题 889|帖子 889|积分 2667

各人好,我是程序视点的小二哥!本日我们来聊聊Java中的锁!

  • synchronized怎么用?
  • 锁是什么?
  • 偏向锁是什么?
  • 锁如何升级?作甚膨胀?
  • 自旋锁何解?
  • 互斥锁怎么来的?
  • 何时要禁用偏向锁和轻量级锁?
带着上面疑问,我们一起来解“锁”疑惑!以上问题会分成几篇文章来讲,方便各人记忆!欢迎持续关注【程序视点】,如许就不会错过之后的精彩内容啦!前言每一个刚接触多线程并发编程的同学,当被问到,如果多个线程同时访问一段代码,发生并发的时间,应该怎么处理?
  我相信闪现在脑海中的第一个解决方案就是用synchronized,让这段代码同一时间只能被一个线程执行。synchronized的疑惑我们也知道,synchronized关键字可以用在方法上,也可以用在代码块上,如果要使用synchronized,我们一般就会如下使用:
public synchronized void doSomething() { //do something here}
大概
synchronized(LockObject) { //do something here}
那么实际上,synchronized关键字到底是怎么加锁的?锁又长什么样子的呢?关于锁,还有一些什么样的概念须要我们去认识,去学习,去明白的呢?以前在学习synchronized的时间,就有文章说, synchronized是一个很重的操作,开销很大,不要容易使用,我们担当了如许的观点,但是为什么说是重的操作呢,为什么开销就大呢?到java1.6之后,java的开辟职员又针对锁机制实现了一些优化,又有文章告诉我们现在颠末优化后,使用synchronized并没有什么太大的问题了,那这又是因为什么原因呢?到底是做了什么优化?那本日我们就尝试着从锁机制实现的角度,来讲述一下synchronized在java虚拟机上面的顺应场景是怎么样的。由于java在1.6之后,引入了一些优化的方案,以是我们讲述synchronized,也会基于java1.6之后的版本。锁对象首先,我们要知道锁实在就是一个对象,java中每一个对象都能够作为锁。 以是我们在使用synchronized的时间,

  • 对于同步代码块,就得指定锁对象。
  • 对于修饰方法的synchronized,默认的锁对象就是当前方法的对象。
  • 对于修饰静态方法的synchronized,其锁对象就是此方法所对应的类Class对象。
我们知道,所谓的对象,无非也就是内存上的一段所在,上面存放着对应的数据,那么我们就要想,作为锁,它跟其它的对象有什么不一样呢?怎么知道这个对象就是锁呢?怎么知道它跟哪个线程关联呢?它又怎么能够控制线程对于同步代码块的访问呢?Markword可以了解到在虚拟机中,对象在内存中的存储分为三部分:

  • 对象头
  • 实例数据
  • 对齐填充
此中,对象头填充的是该对象的一些运行时数据,虚拟机一般用2到3个字宽来存储对象头。

  • 数组对象,会用3个字宽来存储。
  • 非数据对象,则用2个字宽来存储。
其结构简单如下:长度内容说明32/64bitMarkwordhashCode,GC分代年龄,锁信息32/64bitClass Metadata Address指向对象类型数据的指针32/64bitArray Length数组的长度(当对象为数组时)从上表中,我们可以看到,锁相干的信息,是存在称之为Markword中的内存域中。拿以下的代码作为例子,synchonized(LockObject) { //do something here} 在对象LockObject的对象头中,当其被创建的时间,其Markword的结构如下:bit fields 是否偏向锁锁标志位hashage001从上面Markword的结构中,可以看出 所有新创建的对象,都是可偏向的(锁标志位为01),但都是未偏向的(是否偏向锁标志位为0)偏向锁当线程执行到临界区(critical section)时,此时会利用CAS(Compare and Swap)操作,将线程ID插入到Markword中,同时修改偏向锁的标志位。这说明此对象就要被当做一个锁来使用,那么其Markword的内容就要发生变化了。 其结构其会酿成如下:bit fields  是否偏向锁锁标志位threadIdepochage101可以看到,

  • 锁的标志位还是01
  • “是否偏向锁”这个字段酿成了1
  • hash值酿成了线程ID和epoch值
也就是说,这个锁将自己偏向了当火线程,心里默默地藏着线程id, 在这里,我们就引入了“偏向锁”的概念。在此线程之后的执行过程中,如果再次进入大概退出同一段同步块代码,并不再须要去举行加锁大概解锁操作,而是会做以下的步调:

  • Load-and-test,也就是简单判定一下当火线程id是否与Markword当中的线程id是否一致.
  • 如果一致,则说明此线程已经乐成获得了锁,继续执行下面的代码
  • 如果不一致,则要检查一下对象是否还是可偏向,即“是否偏向锁”标志位的值。
  • 如果还未偏向,则利用CAS操作来竞争锁,也即是第一次获取锁时的操作。
  • 如果此对象已经偏向了,并且不是偏向自己,则说明存在了竞争。此时可能就要根据别的线程的环境,可能是重新偏向,也有可能是做偏向撤销,但大部分环境下就是升级成轻量级锁了。
以下是Java开辟职员提供的一张图:
  “偏向锁”是Java在1.6引入的一种优化机制,其核心头脑在于,可以让同一个线程不绝拥有同一个锁,直到出现竞争,才去释放锁。因为颠末虚拟机开辟职员的调查研究,在大多数环境下,总是同一个线程去访问同步块代码,基于如许一个假设,引入了偏向锁,只须要用一个CAS操作和简单地判定比较,就可以让一个线程持续地拥有一个锁。 也正因为此假设,在Jdk1.6中,偏向锁的开关是默认开启的,适用于只有一个线程访问同步块的场景。下篇预告在上面,我们说到,一旦出现竞争,也即有别的一个线程也要来访问这一段代码,偏向锁就不适用于这种场景了。如何解决的呢?下篇文章将带各人了解锁膨胀、锁撤销、轻量级锁等内容!持续关注,如许就不会错过之后的精彩内容啦!如果这篇文章对你有帮助的话,别忘了【点赞】【分享】支持下哦~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

钜形不锈钢水箱

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表