J.U.C (java.util.concurrent) 架构图
synchronized
synchronized 是 Java 中的关键字,是利用锁的机制来实现互斥同步的
synchronized 可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块
三种应用方式:
- 同步实例方法 - 对于普通同步方法,锁是当前实例对象
- 同步静态方法 - 对于静态同步方法,锁是当前类的 Class 对象
- 同步代码块 - 对于同步方法块,锁是 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):
偏向锁的获取和撤销
轻量级锁的膨胀过程
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 信号保证其原子性