一句话说透Java里面的synchronized的原理

383 阅读3分钟

Java中synchronized的原理

一、核心机制

synchronized 是Java实现线程同步的关键字,通过对象锁锁升级策略确保多线程环境下的数据安全与性能优化。


二、锁的实现基础

  1. 对象头与Mark Word

    • 每个Java对象在内存中分为三部分:对象头、实例数据、对齐填充。
    • 对象头中的Mark Word存储锁状态、哈希码、GC年龄等信息。
    • 锁状态通过Mark Word的特定标志位标识,支持锁升级。
  2. 监视器锁(Monitor)

    • 每个对象关联一个Monitor(由C++实现的ObjectMonitor结构)。

    • Monitor包含:

      • EntryList:竞争锁的线程队列。
      • WaitSet:调用wait()的线程队列。
      • Owner:持有锁的线程。
      • 计数器:记录锁的重入次数。

三、锁的四种状态与升级过程

锁状态特点适用场景
无锁对象未被锁定,Mark Word存储哈希码等普通信息。无竞争环境
偏向锁记录线程ID,同一线程多次获取锁无需CAS操作。单线程重复访问
轻量级锁通过CAS自旋尝试获取锁,失败后短暂自旋(避免线程挂起)。低并发,线程交替执行
重量级锁竞争激烈时,线程阻塞并交由操作系统调度(涉及用户态到内核态切换)。高并发,长时间锁竞争

升级流程
无锁 → 偏向锁 → 轻量级锁 → 重量级锁
(根据线程竞争动态调整,不可降级)


四、synchronized的使用方式

  1. 修饰实例方法

    public synchronized void method() { /* 锁对象为当前实例 */ }  
    
  2. 修饰静态方法

    public static synchronized void method() { /* 锁对象为类的Class对象 */ }  
    
  3. 同步代码块

    synchronized (lockObject) { /* 显式指定锁对象 */ }  
    

五、锁的重入性

  • 同一线程可多次获取同一把锁(通过计数器实现)。

    public synchronized void methodA() {  
        methodB(); // 可重入  
    }  
    public synchronized void methodB() { /* ... */ }  
    

六、性能优化与对比

  1. 偏向锁

    • 优点:无竞争时减少CAS开销。
    • 触发:首次获取锁时设置线程ID。
    • 撤销:检测到竞争时升级为轻量级锁。
  2. 轻量级锁

    • 优点:通过自旋减少线程阻塞。
    • 自旋条件:竞争线程少且持有锁时间短。
    • 失败处理:自旋失败后升级为重量级锁。
  3. 重量级锁

    • 缺点:线程阻塞与唤醒开销大(涉及内核态切换)。
    • 适用场景:高并发且锁持有时间长。

七、与ReentrantLock对比

特性synchronizedReentrantLock
锁获取方式自动获取与释放(代码块结束)需手动lock()unlock()
可中断❌ 不支持✅ 支持lockInterruptibly()
公平锁❌ 仅非公平锁✅ 可设置为公平锁
条件变量单一wait()/notify()支持多个Condition

八、总结

  • synchronized 通过对象头锁标志位Monitor机制实现线程同步。

  • 锁升级策略(偏向锁→轻量级锁→重量级锁)平衡性能与安全性。

  • 适用场景

    • 简单同步需求:优先使用synchronized(语法简洁,自动管理锁)。
    • 复杂场景(如超时、公平锁):考虑ReentrantLock

口诀
「对象头里藏玄机,锁的状态分四级
偏向轻量重量级,竞争升级按场景
可重入锁计数器,同步代码保安全!」