synchronized 就是 Java 的“锁王”,它保证多个线程同时访问时,大家不会互相干扰,操作能井然有序。

它的工作原理是通过 JVM 的 “Monitor 锁” 和对象里的 “身份证”——Mark Word 来实现的,确保线程安全。

简单来说:

同一时间,只有拿到锁的线程才能执行操作,其他线程只能排队等着。
通过这种方式,synchronized 确保了每次只有一个线程在进行操作,避免数据混乱或错误。
总结:
synchronized 就是 Java 中的“锁王”,它确保线程之间按顺序执行,不会互相干扰,保证了线程安全。

知识内容

  1. synchronized 的实现原理
    synchronized 的底层实现依赖于 JVM 的 Monitor 机制 和 对象头 中的 Mark Word。

Monitor 机制:每个 Java 对象都可以关联一个 Monitor,它负责管理线程的互斥访问。当一个线程进入 synchronized 代码块时,它会尝试获取对象的 Monitor 锁。如果锁被其他线程持有,当前线程会被阻塞,直到锁被释放。🔒
对象头和 Mark Word:每个 Java 对象在内存中都有一个对象头,其中最重要的部分是 Mark Word。Mark Word 记录了对象的锁状态和锁信息,它会根据锁的状态动态变化。🔍
2. 锁的升级过程
synchronized 的锁会根据线程竞争的激烈程度动态升级,分为以下三个阶段:

2.1 偏向锁
适用场景:只有一个线程访问同步代码块。
实现原理:JVM 会将 Mark Word 中的偏向锁标志设置为 1,并记录当前线程的 ID。当该线程再次访问同步代码块时,JVM 会直接放行,无需加锁操作。
优点:减少锁的开销,适合单线程场景。
撤销:当另一个线程尝试获取锁时,偏向锁会被撤销,升级为轻量级锁。🔄
2.2 轻量级锁
适用场景:多个线程交替访问同步代码块,但没有激烈的竞争。
实现原理:
线程会在自己的栈帧中创建一个 锁记录(Lock Record),并将 Mark Word 的内容复制到锁记录中。
然后通过 CAS(Compare And Swap) 操作将 Mark Word 替换为指向锁记录的指针。
如果 CAS 成功,线程获取锁;如果失败,说明有竞争,锁会升级为重量级锁。
优点:避免线程挂起,减少上下文切换的开销。🚀
2.3 重量级锁
适用场景:多个线程激烈竞争锁资源。
实现原理:JVM 会创建一个 Monitor 对象,并将 Mark Word 指向该对象。未获取到锁的线程会被挂起,进入阻塞状态,直到锁被释放。
缺点:涉及线程挂起和唤醒,开销较大。🚫
3. synchronized 的字节码实现
synchronized 的实现会体现在字节码中,具体分为两种情况:

3.1 修饰方法
在方法的访问标志中加上 ACC_SYNCHRONIZED 标志。
JVM 会在方法调用时自动加锁,方法结束时自动解锁。🔑
3.2 修饰代码块
编译器会在代码块前后插入 monitorenter 和 monitorexit 指令。
monitorenter:尝试获取锁。
monitorexit:释放锁。
示例代码:

public class SyncExample {
public synchronized void syncMethod() {
// 同步方法
}

public void syncBlock() {
    synchronized (this) {
        // 同步代码块
    }
}

}

字节码分析:

同步方法: ACC_SYNCHRONIZED 标志。
同步代码块: monitorenter 和 monitorexit 指令。🔍
4. 锁优化的高级操作

为了提高 synchronized 的性能,JVM 引入了多种优化技术:

4.1 自适应自旋

线程在获取锁失败时,会先进行一段时间的自旋(忙等待),而不是直接挂起。
自旋的时间会根据之前的锁竞争情况动态调整。🔄
4.2 锁消除

JVM通过逃逸分析 判断某些锁是否不会被多线程共享。
如果锁不会被共享,JVM 会直接消除锁操作,减少开销。🔍
4.3 锁粗化

如果一段代码中频繁加锁和解锁,JVM 会将多个锁操作合并为一个,减少锁的开销。🔗

  1. synchronized 的可重入性

synchronized 是 可重入锁,即同一个线程可以多次获取同一把锁。每次进入同步代码块时,锁的计数器加 1;退出时,计数器减 1。
只有当计数器归零时,锁才会被真正释放。🔄

代码演示

  1. 修饰方法的 synchronized

public class SyncMethodExample {
public synchronized void syncMethod() {
System.out.println(“进入同步方法”);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“退出同步方法”);
}

public static void main(String[] args) {
    SyncMethodExample example = new SyncMethodExample();
    Thread t1 = new Thread(example::syncMethod);
    Thread t2 = new Thread(example::syncMethod);
    t1.start();
    t2.start();
}

}

输出结果:
进入同步方法
退出同步方法
进入同步方法
退出同步方法
说明:syncMethod 是同步方法,同一时间只有一个线程可以执行该方法。🔑

  1. 修饰代码块的 synchronized

public class SyncBlockExample {
private final Object lock = new Object();

public void syncBlock() {
    synchronized (lock) {
        System.out.println("进入同步代码块");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("退出同步代码块");
    }
}

public static void main(String[] args) {
    SyncBlockExample example = new SyncBlockExample();
    Thread t1 = new Thread(example::syncBlock);
    Thread t2 = new Thread(example::syncBlock);
    t1.start();
    t2.start();
}

}

输出结果:
进入同步代码块
退出同步代码块
进入同步代码块
退出同步代码块
说明:syncBlock 中的代码块使用 synchronized 修饰,同一时间只有一个线程可以进入该代码块🔑

  1. 可重入锁示例

public class ReentrantExample {
public synchronized void methodA() {
System.out.println(“进入 methodA”);
methodB();
}

public synchronized void methodB() {
    System.out.println("进入 methodB");
}

public static void main(String[] args) {
    ReentrantExample example = new ReentrantExample();
    Thread t = new Thread(example::methodA);
    t.start();
}

}

输出结果:
进入 methodA
进入 methodB
说明:methodA 和 methodB 都是同步方法,methodA 调用 methodB 时不会发生死锁, 因为 synchronized 是可重入锁 🔄

  1. 锁消除示例

public class LockEliminationExample {
public void append(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1).append(s2);
}

public static void main(String[] args) {
    LockEliminationExample example = new LockEliminationExample();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        example.append("Hello", "World");
    }
    long end = System.currentTimeMillis();
    System.out.println("耗时:" + (end - start) + "ms");
}

}

说明:StringBuffer 的 append 方法是同步的,但 JVM 通过逃逸分析发现 sb 对象不会逃逸出方法,因此会消除锁操作🔍

总结
synchronized 是 Java 中实现线程同步的核心工具,其底层通过 Monitor 机制 和
对象头中的 Mark Word 实现锁的管理。
锁的升级过程**(偏向锁 → 轻量级锁 → 重量级锁)和优化技术(自适应自旋、锁消除、锁粗化)**使得
synchronized 在多线程环境中既能保证线程安全,又能兼顾性能。🚀

synchronized 不仅是一个简单的锁,更是一个智能的、动态优化的线程同步工具,适应了现代多核处理器和高并发场景的需求。
🎉 一句话总结:synchronized 是 Java 的“锁王”,
既能保证线程安全,又能通过动态升级和优化技术提升性能,是并发编程的得力助手!🎉