synchronized底层原理深入剖析!object和锁相关的属性方法?锁对象为class的时候锁信息储存在哪里?Monitor 机制?

synchronized底层原理深入剖析

synchronized关键字是实现线程同步的核心机制,其底层原理涉及对象头结构、Monitor监视器、锁升级策略以及JVM优化策略。


一、对象头与锁状态存储
每个Java对象在内存中由对象头(Header)、实例数据和对齐填充组成,锁信息存储在对象头的Mark Word字段中。
Mark Word结构(64位JVM为例):

锁状态存储内容(64位)
无锁哈希码(31位)、分代年龄(4位)、锁标志位(01)
偏向锁线程ID(54位)、Epoch(2位)、锁标志位(01)
轻量级锁指向栈中锁记录的指针(62位)、锁标志位(00)
重量级锁指向Monitor的指针(62位)、锁标志位(10)
  • 锁标志位:标识当前锁状态(01/00/10),决定Mark Word的解析方式。

二、Monitor监视器机制
每个对象关联一个Monitor(由C++实现的ObjectMonitor结构),其核心组成:

  1. Owner:持有锁的线程,初始为NULL
  2. EntryList:竞争锁失败的线程队列(阻塞态)。
  3. WaitSet:调用wait()后释放锁的线程队列(等待唤醒)。

工作流程:

  1. 线程通过monitorenter指令尝试获取锁:
    • 若Owner为空,CAS设置Owner为当前线程,_recursions计数器置1(支持重入)。
    • 若Owner是当前线程,_recursions计数器递增。
    • 竞争失败则进入EntryList等待。
  2. 释放锁时执行monitorexit,递减计数器直至0,唤醒EntryList中的线程。

三、锁升级策略(自适应优化)
JVM根据线程竞争动态调整锁状态,降低同步开销:

  1. 偏向锁(Biased Locking)

    • 适用场景:单线程重复获取锁。
    • 实现:通过CAS将线程ID写入Mark Word,后续无需同步操作。
    • 撤销:检测到其他线程竞争时(通过Epoch机制),升级为轻量级锁。
  2. 轻量级锁(Lightweight Locking)

    • 适用场景:低竞争、短临界区。
    • 实现:线程在栈帧中创建Lock Record,CAS将Mark Word复制到Lock Record并指向它。
    • 自旋失败:超过默认自旋次数(JDK6前固定10次,后改为自适应),升级为重量级锁。
  3. 重量级锁(Heavyweight Locking)

    • 实现:依赖操作系统的互斥量(Mutex)实现线程阻塞,通过Monitor的EntryList管理竞争线程。
    • 开销:涉及用户态到内核态的切换,性能损耗较高。

四、synchronized的字节码实现
通过monitorentermonitorexit指令实现同步控制:

public void syncMethod() {
    synchronized(obj) {
        // 临界区代码 
    }
}

编译后的字节码:

0: aload_1              // 加载obj到操作数栈 
1: dup                  
2: astore_2             
3: monitorenter         // 尝试获取锁 
4: aload_2             
5: monitorexit          // 正常退出释放锁 
6: goto          14 
9: astore_3             
10: aload_2             
11: monitorexit         // 异常退出释放锁(确保锁释放)
12: aload_3             
13: athrow              
14: return 
  • 异常处理表:保证无论代码块是否抛出异常,都会执行monitorexit

五、JVM优化策略

  1. 锁粗化(Lock Coarsening)
    合并多个相邻同步块,减少锁获取/释放次数。

    // 优化前 
    synchronized(obj) { ... }
    synchronized(obj) { ... }
    // 优化后 
    synchronized(obj) { ...; ... }
    
  2. 锁消除(Lock Elimination)
    通过逃逸分析,移除不可能存在共享资源竞争的锁。

    public String concat(String s1, String s2) {
        StringBuffer sb = new StringBuffer(); // 局部变量,无竞争 
        sb.append(s1);
        sb.append(s2);
        return sb.toString();
    }
    
  3. 自适应自旋(Adaptive Spinning)
    根据历史自旋成功率动态调整自旋次数,避免CPU空转。


六、性能影响与应用场景

  1. 低竞争场景:偏向锁和轻量级锁几乎无性能损耗。
  2. 高竞争场景:重量级锁的线程阻塞可能成为性能瓶颈(建议改用ReentrantLock或CAS)。
  3. 锁粒度控制:尽量缩小同步代码块范围,减少锁持有时间。

总结
synchronized通过对象头存储锁状态、Monitor实现互斥访问、锁升级策略平衡性能,成为Java线程安全的基石。其设计体现了JVM对“快速路径(Fast Path)”和“慢速路径(Slow Path)”的智能切换。开发中需结合竞争强度、临界区大小等因素,合理选择同步方案。

synchronized锁对象为class的时候锁信息储存在哪里?

当使用 synchronized 锁对象为 Class 时(例如 synchronized(MyClass.class) 或修饰静态方法),锁信息存储的位置与普通对象锁的存储机制一致,但其具体载体是 Class 类对应的 Java 对象头。

Monitor 机制

Monitor 机制是 synchronized 关键字实现线程同步的核心底层机制,其工作原理可分为以下几个关键点:


一、Monitor 的基本结构

  1. 对象关联性
    每个 Java 对象(包括类对象)在创建时都会关联一个 Monitor(监视器),用于管理线程对同步代码的访问。

  2. 核心组成部分

    • Owner(持有者):当前持有锁的线程。
    • Entry Set(入口队列):等待获取锁的线程队列。
    • Wait Set(等待队列):通过 wait() 方法主动释放锁并进入阻塞状态的线程队列。

二、互斥访问的实现原理

  1. 锁的获取与释放

    • 当线程执行到 synchronized 代码块时,会尝试通过 CAS 操作获取对象的 Monitor。
    • 若 Monitor 的 Owner 为空,线程成为 Owner 并执行同步代码;否则,线程进入 Entry Set 等待。
  2. 重入性支持
    Monitor 通过计数器(_recursions)支持锁重入,同一线程多次获取锁时计数器递增,释放时递减至零才会完全释放锁。


三、线程协作机制(wait/notify)

  1. wait() 的运作
    线程调用 wait() 后,会释放锁并进入 Wait Set,等待其他线程通过 notify()notifyAll() 唤醒。

  2. notify() 的触发
    唤醒 Wait Set 中的一个线程,该线程会从阻塞状态转移到 Entry Set,重新竞争锁。

object和锁相关的属性方法

与锁相关的核心机制通过 Object 类的方法和底层对象结构实现,主要涉及线程同步、锁状态管理及线程协作。以下是详细说明:


一、Object 类中与锁相关的核心方法

  1. wait() 系列方法

    • 作用:使当前线程释放锁并进入等待状态,直到其他线程调用 notify()/notifyAll() 或超时。
    • 重载方法:
      • wait():无限等待。
      • wait(long timeout):等待指定毫秒。
      • wait(long timeout, int nanos):更高精度超时控制。
    • 使用条件:必须在同步代码块(synchronized)内调用,否则抛出 IllegalMonitorStateException
  2. notify()notifyAll()

    • notify():随机唤醒一个等待该对象锁的线程,被唤醒的线程需重新竞争锁。
    • notifyAll():唤醒所有等待该对象锁的线程。
    • 注意事项:同样必须在同步代码块内调用。
<think>我们正在讨论Javasynchronized关键字的对象。根据引用[1]引用[2]: - synchronized定的对象可以是实例对象对象Class对象)。 - 当修饰实例方法时,定的是当前实例对象(this)。 - 当修饰静态方法时,定的是当前Class对象。 - 当修饰代码块时,可以指定定的对象(任意对象)。 因此,synchronized定的对象取决于使用方式: 1. 实例方法定调用该方法的实例对象(this)。 2. 静态方法定该Class对象(如MyClass.class)。 3. 代码块:定括号内指定的对象。 注意:每个对象都有一个与之关联的监视器monitor),当线程进入synchronized代码块时,会尝试获取该对象的监视器,如果获取成功则执行代码,否则阻塞。 另外,引用[3]提到,在JVM中,对象的监视器ObjectMonitor对象实现,它记录了的状态、持有的线程、等待队列等信息。 下面用代码示例说明: 示例1:实例方法 ```java public class Example { public synchronized void instanceMethod() { // 定的是当前实例对象,即this } } ``` 示例2:静态方法 ```java public class Example { public static synchronized void staticMethod() { // 定的是Example.classClass对象) } } ``` 示例3:代码块 ```java public class Example { private final Object lock = new Object(); public void method() { synchronized (lock) { // 定的是lock对象 } } } ``` 重要规则: - 多个线程访问同一个对象synchronized方法/代码块会互斥。 - 不同对象synchronized方法/代码块不会互斥(因为对象不同)。 - 实例是不同的,因此不会互斥。 例如: 线程1:调用obj1的实例同步方法定obj1) 线程2:调用obj2的实例同步方法定obj2) -> 不会互斥,因为对象不同 线程3:调用的静态同步方法对象) -> 与上面两个都不互斥,因为对象不同 但是,如果多个线程同时访问同一个对象(或同一个)的synchronized方法/代码块,则会发生互斥(阻塞等待)。 引用[1]提到:synchronized住的是一个对象或者本身也是对象,即Class对象),而不是方法或者代码段。因此,的粒度是对象级别。 总结:synchronized定的对象取决于使用方式,具体为: - 实例方法:当前实例对象(this) - 静态方法:当前Class对象 - 代码块:显式指定的对象 这样,通过定不同的对象,可以实现不同粒度的同步控制。 相关问题: 1. 如何避免synchronized导致的死? 2. synchronized升级过程是怎样的? 3. 为什么说synchronized是非公平?</think>### Javasynchronized关键字的对象详解 #### 一、对象的本质 synchronized定的**不是方法或代码段**,而是**对象**(本质也是对象)。根据使用方式不同,对象分为两: 1. **实例对象**:定具体对象实例 2. **对象**:Class对象(如`MyClass.class`) #### 二、不同使用场景的对象 | 使用方式 | 对象 | 示例代码 | |------------------------------|--------------------------|--------------------------------------------------------------------------| | **修饰实例方法** | 当前实例对象 (`this`) | `public synchronized void method() {...}` [^1] | | **修饰静态方法** | Class对象 | `public static synchronized void staticMethod() {...}` | | **代码块指定实例对象** | 显式指定的对象实例 | `synchronized(obj) {...}` | | **代码块指定Class对象** | Class对象 | `synchronized(MyClass.class) {...}` | #### 三、互斥规则 1. **相同对象互斥**: ```java // 线程1线程2访问同一个对象的同步方法会互斥 MyObject obj = new MyObject(); new Thread(obj::syncMethod).start(); // 线程1 new Thread(obj::syncMethod).start(); // 线程2 ❌ 阻塞 ``` 2. **不同对象不互斥**: ```java // 线程1线程2访问不同对象的同步方法不互斥 MyObject obj1 = new MyObject(); MyObject obj2 = new MyObject(); new Thread(obj1::syncMethod).start(); // 线程1 new Thread(obj2::syncMethod).start(); // 线程2 ✅ 并行执行 ``` 3. **与实例不互斥**: ```java class MyClass { public synchronized void instanceMethod() {...} // 实例 public static synchronized void staticMethod() {...} // } // 可同时执行(对象不同) new Thread(() -> new MyClass().instanceMethod()).start(); new Thread(MyClass::staticMethod).start(); // ✅ 并行执行 ``` #### 四、底层实现原理 在JVM中,每个对象关联一个**监视器Monitor)**,由`ObjectMonitor`实现(C++): ```cpp ObjectMonitor() { _count = 0; // 获取次数 _owner = NULL; // 持有的线程 _EntryList = NULL; // 阻塞线程队列 _WaitSet = NULL; // 调用wait()的线程队列 } ``` 当线程执行`synchronized`代码时: 1. 通过`monitorenter`指令尝试获取对象的监视器 2. 若`_owner`为null,则获得并设置`_owner`为当前线程 3. 若已被占用,则进入`_EntryList`阻塞等待[^3] #### 五、关键特性 1. **可重入性**:同一线程可重复获取同一把 ```java public synchronized void methodA() { methodB(); // ✅ 可重入 } public synchronized void methodB() {...} ``` 2. **粒度控制**: - 细粒度定独立对象减少竞争 ```java private final Object lock1 = new Object(); private final Object lock2 = new Object(); void method() { synchronized(lock1) { /* 操作资源1 */ } synchronized(lock2) { /* 操作资源2 */ } } ``` 3. **释放保证**: - 正常执行:通过`monitorexit`释放 - 异常退出:JVM确保释放(通过异常处理表) #### 六、使用建议 1. **避免字符串常量**:字符串常量池可能引发意外竞争 ```java // 危险操作(不同模块可能意外共享synchronized("GLOBAL_LOCK") {...} ``` 2. **优先选择私有对象**: ```java private final Object myLock = new Object(); // ✅ 安全 ``` 3. **使用场景**: - 静态数据保护 - 级别操作(如加载资源) > **关键原则**:的互斥范围取决于**对象的唯一性**,而非代码位置[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值