写点什么

Java Core「2」synchronized 关键字

作者:Samson
  • 2022 年 5 月 22 日
  • 本文字数:1417 字

    阅读完需:约 5 分钟

Java Core「2」synchronized 关键字

01-synchronized 作用在哪里?

无论作用在哪里,都需要依赖某个对象,或者更进一步,依赖该对象的 monitor。


  1. 方法

  2. 对象方法,方法所属对象,即this

  3. 静态方法,类对象,即 *.class 对应的Class对象。

  4. 代码块,作用在代码块上时,需要显式地指定依赖的对象。


通过分析 *.class 文件,我们更进一步探究,对于:


  1. 静态方法,其 falgs 中包含:ACC_STATIC, ACC_SYNCHRONIZED

  2. 对象方法,其 flags 中包含:ACC_SYNCHRONIZED

  3. 代码块,其字节码前后被插入了monitorentermonitorexit指令。


JVM 规范中对monitorenter的描述如下:


The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.

• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.

• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.


从上面的描述中可以看出,同一个线程是可以再次获取 monitor 锁的(即可重入)。

02-synchronized 与对象的 mark word

在 HotSpot JVM 中,对象的运行时表示分为三部分:mark word + klass word + padding。其中 mark word 主要存储了对象的哈希码、锁信息、GC 元数据等。


32 位和 64 位系统中,mark word 是有区别的:




从前面一节的介绍中我们知道,synchronized依赖于对象的 monitor 锁,其信息记录在对象的 mark word 中。


锁膨胀的方向为:无锁 → 偏向锁 → 轻量级锁 → 重量级锁 (一旦膨胀不可降级)。详细的升级过程参考[1]。


[1] 再谈synchronized锁升级

03-synchronized 如何保证可见性?

synchronized对可见性的保证是通过下面 happens-before 规则来保证的。


An unlock on a monitor happens-before every subsequent lock on that monitor.



借用 pdai.tech 上的一张图片和解释:


线程 A 释放锁 happens-before 线程 B 加锁,蓝色的则是通过程序顺序规则和监视器锁规则推测出来 happens-befor 关系,通过传递性规则进一步推导的 happens-before 关系。


如果 A happens-before B,则 A 的执行结果对 B 可见,并且 A 的执行顺序先于 B

04-synchronized vs. Lock

java.util.concurrent 包中提供了一些同步工具(例如 ReentrantLock )来应对更复杂的同步场景。我们从以下几个方面对比一下synchronized与 JUC 中的锁。


  • 简易性。synchronized使用起来更简单。而且从 JDK 1.6 之后,synchronized效率提高了不少。如果同步场景不太复杂,建议优先使用synchronized

  • 乐观锁/悲观锁。synchronized是一种悲观锁,总是尝试获取对象的 monitor 锁,无法获取的线程将被阻塞。而 JUC 中的锁是能够实现乐观锁的。例如,如果lock.tryLock()获取成功,...,如果失败 ...

  • 公平锁/非公平锁。synchronized是一种非公平锁,而ReentrantLock是可以指定公平锁或非公平锁的。

  • 是否可中断。synchronized不可以中断,当多个线程因临界资源形成死锁时,无法通过剥夺锁的方式解除死锁。JUC 中的锁是可设置中断和超时。

  • 灵活性。synchronized加锁和释放锁的时机单一,而且仅支持一个条件。ReentrantLock等可设置多个Condition


历史文章

Java Core「1」JUC- 线程基础

发布于: 刚刚阅读数: 2
用户头像

Samson

关注

还未添加个人签名 2019.07.22 加入

还未添加个人简介

评论

发布
暂无评论
Java Core「2」synchronized 关键字_学习笔记_Samson_InfoQ写作社区