高并发必备(三)!深入剖析synchronized底层原理:从Java对象头到锁升级全解析

目录

1. 基本使用方式

2. 底层实现原理 

2.1 Java对象头

2.2 Monitor机制

2.3 锁升级过程

3. 字节码层面分析

3.1 同步代码块

3.2 同步方法 

4. 锁优化技术

5. 与其他锁的比较

6. 使用建议


synchronized是Java中实现线程同步的关键字,它提供了内置的锁机制,可以保证同一时刻只有一个线程能够访问被同步的代码块或方法。下面我将从多个层面详细解析synchronized的底层实现原理。

1. 基本使用方式

synchronized有三种基本使用方式:

  1. 同步实例方法:锁是当前实例对象

    public synchronized void method() {
        // ...
    }
  2. 同步静态方法:锁是当前类的Class对象

    public static synchronized void staticMethod() {
        // ...
    }
  3. 同步代码块:锁是括号里配置的对象

    public void method() {
        synchronized(obj) {
            // ...
        }
    }

2. 底层实现原理 

synchronized的底层实现主要依赖于JVM中的Monitor(监视器锁)机制,具体实现涉及以下几个方面:

2.1 Java对象头

在JVM中,每个对象都有一个对象头,其中包含两部分信息:

  1. Mark Word:存储对象自身的运行时数据

  2. Klass Pointer:指向对象所属类的元数据的指针

synchronized使用的锁信息就存储在Mark Word中。Mark Word在不同状态下会存储不同的内容:

锁状态存储内容标志位
无锁对象的hashCode、分代年龄等01
偏向锁偏向线程ID、偏向时间戳、分代年龄等01
轻量级锁指向栈中锁记录的指针00
重量级锁指向互斥量(重量级锁)的指针10
GC标记11

2.2 Monitor机制

Monitor是JVM提供的一种同步机制,每个Java对象都可以关联一个Monitor对象。Monitor的主要组成部分包括:

  1. Owner:持有该Monitor的线程

  2. EntryList:处于阻塞状态的线程队列

  3. WaitSet:处于等待状态的线程队列(调用了wait()方法的线程)

当线程执行到synchronized代码块时:

  1. 尝试获取对象的Monitor

  2. 如果获取成功,成为Monitor的Owner

  3. 如果获取失败,进入EntryList等待

2.3 锁升级过程

为了减少锁操作的开销,JVM实现了锁的升级机制:

  1. 无锁状态:初始状态

  2. 偏向锁:当第一个线程访问同步块时,会进入偏向模式

    • 在Mark Word中记录线程ID

    • 后续该线程进入同步块时不需要任何同步操作

  3. 轻量级锁:当有第二个线程尝试获取锁时,偏向锁升级为轻量级锁

    • 线程通过CAS操作竞争锁

    • 适用于线程交替执行的场景

  4. 重量级锁:当竞争激烈时(自旋获取锁失败),轻量级锁升级为重量级锁

    • 线程会被阻塞,进入内核态等待

    • 性能开销最大

3. 字节码层面分析

从字节码角度看,synchronized的实现:

  • 同步代码块使用monitorentermonitorexit指令

  • 同步方法使用ACC_SYNCHRONIZED标志

3.1 同步代码块

public void syncBlock() {
    synchronized(this) {
        System.out.println("sync block");
    }
}

对应的字节码: 


public void syncBlock();
  Code:
     0: aload_0
     1: dup
     2: astore_1
     3: monitorenter          // 进入同步块
     4: getstatic     #2      // Field java/lang/System.out:Ljava/io/PrintStream;
     7: ldc           #3      // String sync block
     9: invokevirtual #4      // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    12: aload_1
    13: monitorexit           // 正常退出同步块
    14: goto          22
    17: astore_2
    18: aload_1
    19: monitorexit           // 异常退出同步块
    20: aload_2
    21: athrow
    22: return

3.2 同步方法 

public synchronized void syncMethod() {
    System.out.println("sync method");
}

对应的字节码: 

public synchronized void syncMethod();
  flags: ACC_PUBLIC, ACC_SYNCHRONIZED
  Code:
     0: getstatic     #2      // Field java/lang/System.out:Ljava/io/PrintStream;
     3: ldc           #5      // String sync method
     5: invokevirtual #4      // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     8: return

4. 锁优化技术

JVM对synchronized进行了多种优化:

  1. 自旋锁:竞争锁失败的线程不立即阻塞,而是执行忙循环(自旋)

  2. 锁消除:JIT编译器通过逃逸分析消除不必要的锁

  3. 锁粗化:将连续的多个锁操作合并为一个更大的锁操作

  4. 适应性自旋:根据历史情况动态调整自旋时间

5. 与其他锁的比较

特性synchronizedReentrantLock
实现方式JVM内置JDK实现
获取锁方式自动获取释放手动lock/unlock
锁类型非公平锁可公平/非公平
条件变量单一多个
中断响应不支持支持
性能优化后相当相当

6. 使用建议

  1. 优先使用synchronized,除非需要高级功能

  2. 尽量缩小同步代码块的范围

  3. 避免在同步块中执行耗时操作

  4. 注意死锁问题,避免嵌套获取多个锁

<<Java高并发实战:原理、源码与性能调优>>
<<Java集合框架深度解析:从源码到实战应用>>

📣 互动邀请
如果本文对您有帮助,请不要吝啬您的支持:

👍 点赞 - 您的点赞是我持续创作的最大动力!
🔔 关注 - 获取更多Java核心技术解析和实战技巧
💬 评论 - 有任何问题或建议欢迎留言讨论

🚀 下期预告
接下来我将继续深入讲解:

                《ReentrantLock底层原理详解:从源码到实战全面解析》

              

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值