synchronized

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的结构也变为轻量级锁的结构。轻量级锁适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场合,就会导致轻量级锁升级为重量级锁。

自旋锁

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

锁消除

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤独地卜师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值