欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

synchronized

时间:2023-08-02
synchronized

synchronized关键字被编译成字节码后,会被翻译成monitorenter和monitorexit两条指令,分别在同步块逻辑代码的起始位置和结束位置。
每个同步对象都有自己的Monitor。
1.使用Monitor.Enter得到对象Object的监视器锁,
2.如果Monitor.Enter成功执行同步业务
3.执行完业务释放锁 Moniter.Exit
4.如果第2步失败,把这次请求的线程放到同步队列中SynchronizedQueue中,当第三步执行完,出队列,再尝试获取锁。

当多个线程同时访问一段同步代码时:
1.首先会进入_EntryList集合,当线程获取到对象的monitor后,进入_Owner区域并把monitor中的owner变量设置为当前线程,同时monitor中的计数器count加1;
2.若线程调用wait方法,将释放当前持有的monitor,owner变量恢复为null,count减1,同时该线程进入WaitSet集合中等待被唤醒;
3.若当前线程执行完毕,也将释放monitor锁并复位count的值,以便其他线程进入获取monitor;

Monitor对象存在于每个java对象的对象头Mark Word中,Synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,同时notify notifyAll wait等方法会使用到Monitor锁对象,所以必须在同步代码块中使用。

对象的内存布局

对象在内存中存储的布局可以分为三块区域:对象头、实例数据、对齐填充。
1.对象头:hash码,对象所属的年代,对象锁,锁状态标志偏向锁(线程)ID,偏向时间,数组长度(数组对象)等。java对象头一般占有2个机器码,但是如果对象是数组类型,则需要3个机器码,因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确定数组的大小,所以用一块来记录数组的长度。
2.实例数据:存放类的属性数据信息,包括父类的属性信息;
3.对齐填充:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅为了字节对齐。

Mark Word

对象头中的 MarkWord是实现轻量级锁和偏向锁的关键。

偏向锁

偏向锁是Java6之后加入的,它是一种针对枷锁操作的优化手段,在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁的代价而引入偏向锁。如果一个线程获得了偏向锁,那么锁就进入偏向模式,Mark Word的结构也变为偏向锁结构,当这个线程再次请求锁时,无需做任何同步操作。如果锁的竞争比较激烈,偏向锁就失效了,偏向锁失效后,先升级为轻量级锁

轻量级锁

如果偏向锁失效,虚拟机并不会立即升级为重量级锁,他先尝试一种轻量级锁的优化手段也是java6加入的,此时Mark Word的结构也变为轻量级锁的结构。轻量级锁适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场合,就会导致轻量级锁升级为重量级锁。

自旋锁

轻量级锁失败后,还会进行一项称为自旋锁的优化手段。如果经过自旋锁还不能获得锁,那就会将线程在操作系统层面挂起,就升级为重量级锁了。

锁消除

锁消除依据的是逃逸分析的数据支持。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。