Java中synchronized的实现原理是什么?
synchronized 就是 Java 的“锁王”,它保证多个线程同时访问时,大家不会互相干扰,操作能井然有序。
它的工作原理是通过 JVM 的 “Monitor 锁” 和对象里的 “身份证”——Mark Word 来实现的,确保线程安全。
简单来说:
同一时间,只有拿到锁的线程才能执行操作,其他线程只能排队等着。
通过这种方式,synchronized 确保了每次只有一个线程在进行操作,避免数据混乱或错误。
总结:
synchronized 就是 Java 中的“锁王”,它确保线程之间按顺序执行,不会互相干扰,保证了线程安全。
知识内容
- 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 会将多个锁操作合并为一个,减少锁的开销。🔗
- synchronized 的可重入性
synchronized 是 可重入锁,即同一个线程可以多次获取同一把锁。每次进入同步代码块时,锁的计数器加 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 是同步方法,同一时间只有一个线程可以执行该方法。🔑
- 修饰代码块的 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 修饰,同一时间只有一个线程可以进入该代码块🔑
- 可重入锁示例
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 是可重入锁 🔄
- 锁消除示例
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 的“锁王”,
既能保证线程安全,又能通过动态升级和优化技术提升性能,是并发编程的得力助手!🎉
