J.U.C (java.util.concurrent) 架构图

synchronized

synchronized 是 Java 中的关键字,是利用锁的机制来实现互斥同步的

synchronized 可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块

三种应用方式:

  1. 同步实例方法 - 对于普通同步方法,锁是当前实例对象
  2. 同步静态方法 - 对于静态同步方法,锁是当前类的 Class 对象
  3. 同步代码块 - 对于同步方法块,锁是 synchonized 括号里配置的对象

实现原理

synchronized 代码块是由一对 monitorenter 和 monitorexit 指令实现的,Monitor 对象 是同步的基本实现单元

任何对象都有一个 monitor 与之关联,当且一个 monitor 被持有后,它将处于锁定状态。 线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 的所有权,即尝 试获得对象的锁

synchronized 修饰同步方法时,会设置一个 ACC_SYNCHRONIZED 标志。当方法调用时,调 用指令将会检查该方法是否被设置 ACC_SYNCHRONIZED 访问标志。如果设置了该标志,执行 线程将先持有 Monitor 对象,然后再执行方法。

锁的不同状态

在 JDK 1.6 之前,Monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户 态到内核态的切换,所以同步操作是一个无差别的重量级操作

在 JDK1.6 JVM 中,对象实例在堆内存中被分为了三个部分:对象头、实例数据和对齐填充。 其中 Java 对象头由 Mark Word、指向类的指针以及数组长度三部分组成

Mark Word 记录了对象和锁有关的信息,如下图所示(64 位 JVM):

偏向锁的获取和撤销

participant thread1
participant MarkWord
participant thread2
thread1 -> thread1 : 访问临界区
thread1 -> MarkWord : 检查对象头是否设置了thread1 ID
thread1 <- MarkWord : 线程ID为空
thread1 -> MarkWord : CAS替换线程ID, 指向自己
thread2 -> thread2 : 访问临界区
thread2 -> MarkWord : 检查对象头是否设置了thread2 ID
thread2 <- MarkWord : 线程ID指向thread1
thread1 -> thread1 : 执行临界区代码
thread2 -> MarkWord : CAS替换线程ID
thread2 <- MarkWord : 不成功
thread2 -> thread1 : 撤销偏向锁
thread1 -> thread1 : 暂停线程
thread1 -> MarkWord : 解锁,将线程ID设为空
thread1 -> thread1 : 恢复线程

轻量级锁的膨胀过程

(*) --> 线程1
--> 访问临界区
--> 获取偏向锁
-->[线程2争夺,获取失败] CAS修改Mark Word, 获取轻量级锁
-->[成功] 执行临界区代码
-->[执行完毕] 释放锁
-->[唤醒] 线程阻塞
--> (*)
(*) --> 线程2
--> 访问临界区
--> 获取偏向锁
-->[获取失败] 自旋获取轻量级锁
-->[循环一定次数后失败] 膨胀为重量级锁
--> 线程阻塞
--> 唤醒后继续尝试获取锁
--> (*)

volatile

volatile 是轻量级的 synchronized,它在多处理器开发中保证了共享变量的“可见性”

被 volatile 修饰的变量,具备以下特性:

  • 线程可见性 - 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个 共享变量,另外一个线程能读到这个修改的值
  • 禁止指令重排序
  • 不保证原子性,因此不能确保线程安全

通常来说,使用 volatile 必须具备以下 2 个条件:

  • 对变量的写操作不依赖于当前值
  • 该变量没有包含在具有其他变量的表达式中

CAS

CAS(Compare and Swap)是由硬件支持的原子性操作,字面意思为比较并交换。CAS 有 3 个操作数,分别是:内存值 M,期望值 E,更新值 U。当且仅当内存值 M 和期望值 E 相等 时,将内存值 M 修改为 U,否则什么都不做

CAS 在 Java 中的应用通常是:原子类和自旋锁

实现原理:Java 主要利用 Unsafe 这个类提供的 CAS 操作。Unsafe 的 CAS 依赖的是 JVM 针对不同的操作系统实现的硬件指令 Atomic::cmpxchg。Atomic::cmpxchg 的实现使用了汇 编的 CAS 操作,并使用 CPU 提供的 lock 信号保证其原子性