Java Lock

锁分类

  • 公平锁/非公平锁

    • 公平锁 入锁前如果阻塞会自动排队到链表最后面;

      public ReentrantLock(boolean fair) {
          sync = fair ? new FairSync() : new NonfairSync();
      }
    • 非公平锁 谁先抢到,谁先执行;优势为吞吐量较公平锁大,Synchronized为公平锁;

  • 可重入锁

    • 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。

    • ReentrantLock 和 Synchronized 是可重入锁

    • Code View

      synchronized void setA() throws Exception{
          Thread.sleep(1000);
          setB();
      }
      
      synchronized void setB() throws Exception{
          Thread.sleep(1000);
      }
  • 独享锁/共享锁(排它锁)

    • 独享锁是指该锁一次只能被一个线程所持有。ReentrantLock & Synchronized & ReentrantReadWriteLock writeLock().lock()

    • 共享锁与独占锁相反,可被多个线程持有。ReentrantReadWriteLock readLock().lock()

      `

  • 互斥锁 / 读写锁

    • 互斥锁:与独享锁同义,一次仅限一个线程(如 ReentrantLock)。

    • 读写锁:读共享、写排它(如 ReentrantReadWriteLock)。

  • 乐观锁 / 悲观锁

    • 悲观锁:默认加锁后再做操作,典型如 SynchronizedReentrantLock

    • 乐观锁:无锁操作前先检查冲突,常见于 CAS

  • 分段锁

    • 典型示例 ConcurrentHashMap(JDK 1.7 以前),将数据分段,锁粒度更细,减少锁冲突。

  • 偏向锁/轻量级锁/重量级锁

在 HotSpot JVM 中,一个对象的内存布局通常可分为 3 部分:

1. 对象结构

  1. 对象头(Object Header)

    • Mark Word

      • 64 位(在 64 位 JVM 下)。

      • 存储对象自身的状态信息(HashCode、GC 分代年龄、锁标记位、偏向锁线程 ID 等)。

    • 类型指针(Klass Pointer)

      • 指向对象所属的类元数据(Klass 结构)。

      • 由于开启了压缩指针(Compressed Oops),常用 32 位存储,然后在访问时进行解压。

  2. 实例数据:对象实际字段(含父类继承的字段)。

  3. 对齐填充:满足 8 字节对齐的要求。

2. Mark Word 典型布局

以下是一种常见的 64 位 Mark Word 分布(无锁、已计算 HashCode 场景)示例,实际布局可能因 JVM 版本/配置略有不同

位分布(从高位到低位)
含义

25 bit

存储对象的 25 位 HashCode

4 bit

对象的分代年龄(GC Age)

1 bit

偏向锁标志(Biased Lock)

2 bit

锁标记位(Lock Bits)

32 bit

其他(如空闲或备用字段)

  • Lock Bits(锁标记位) 一般占用 2 bit

  • 若偏向锁启用,则还需要额外 1 bit 用来区分“是否偏向”。

根据锁状态的不同(无锁、偏向锁、轻量级锁、重量级锁),Mark Word 中各段的具体含义会动态变化。尤其在“偏向锁”场景下,Mark Word 会记录 线程 IDepoch 等信息,而不再是简单的 HashCode。

3. 锁标记(Lock Bits)作用

HotSpot 会在 Mark Word 的最低 2 位(有时加上倒数第 3 位的偏向标记),来表示对象当前的锁定状态。常见取值如下表所示(不同版本略有差异,但概念相同):

Lock Bits

Biased Flag

对象锁状态

说明

01

1

偏向锁 (Biased)

无竞争场景,Mark Word 存储线程 ID

00

?

轻量级锁 (Lightweight)

通过 CAS + 自旋获取锁,Mark Word 存储指向锁记录指针

10

?

重量级锁 (Heavyweight)

互斥量 (Monitor) 同步,线程阻塞/唤醒

01

0

无锁 (Unlocked)

初始状态或已计算过 HashCode

11

-

GC 标记 (Marked)

可能是 CMS/G1 阶段做标记等(或其他特殊用途)

  • 具体二进制值对应关系,需结合偏向标志位 (Biased Flag) 一起解读。

  • 当偏向锁被撤销后,会置为轻量级锁或直接升级到重量级锁。

  • 11” 状态通常在垃圾回收标记、移动对象等场景使用。


4. 总结

整个synchronized锁流程如下:

  1. 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁

  2. 如果不是,则使用CAS将当前线程的ID替换Mard Word,如果成功则表示当前线程获得偏向锁,置偏向标志位1

  3. 如果失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。

  4. 当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,当前线程获得锁

  5. 如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

  6. 如果自旋成功则依然处于轻量级状态。

  7. 如果自旋失败,则升级为重量级锁。

  • 自旋锁

    • 【JDK1.4.2~JDK1.6】之间 -XX:+UseSpinning开启自旋锁

    • 【JDK1.7】后,去掉此参数,由jvm控制;

锁消除 锁粗化

类锁 对象锁

无锁

Last updated

Was this helpful?