Synchronized
是Java中最常用的内置锁机制,用于确保多线程环境下的同步。其底层原理涉及到JVM(Java虚拟机)和字节码指令。以下是 synchronized
的底层工作原理的详细介绍:
1. 基本概念
- 对象头(Object Header):每个Java对象在内存中都有一个对象头,包含了锁标志位(Lock Word)。
- Monitor:每个对象都有一个与之关联的监视器(Monitor),监视器是由C++实现的对象,它主要用于实现对象的同步机制。
2. 锁的状态
锁的状态在对象头中的锁标志位表示,可以有以下几种状态:
- 无锁(Unlocked):没有线程持有锁。
- 偏向锁(Biased Lock):当一个线程第一次访问一个对象时,对象头中的锁标志位会变为偏向锁,表明该线程持有锁。
- 轻量级锁(Lightweight Lock):当第二个线程尝试获取锁时,偏向锁会升级为轻量级锁。
- 重量级锁(Heavyweight Lock):当轻量级锁竞争失败时,会升级为重量级锁。重量级锁会阻塞所有尝试获取锁的线程,直到持有锁的线程释放锁。
3. synchronized 的实现
synchronized
关键字可以用于方法或代码块中。其底层实现如下:
3.1 同步代码块
synchronized (this) {
// critical section
}
同步代码块的实现依赖于字节码指令 monitorenter
和 monitorexit
:
- monitorenter:当线程进入同步代码块时执行,尝试获取对象的Monitor锁。
- monitorexit:当线程退出同步代码块时执行,释放对象的Monitor锁。
每个Monitor对象都包含一个计数器(Owner Count)和一个指向持有该锁的线程的指针(Owner)。
3.2 同步方法
public synchronized void method() {
// critical section
}
同步方法使用 ACC_SYNCHRONIZED
标志。当方法调用时,JVM会自动获取对象的Monitor锁,方法执行完毕后释放锁。
4. 锁的升级与膨胀
- 偏向锁:偏向锁会在无竞争的情况下将锁偏向第一个获取它的线程。当同一个线程再次进入同步块时,不需要进行同步操作。
- 轻量级锁:当第二个线程尝试获取偏向锁时,会触发偏向锁升级为轻量级锁。这时会在当前线程栈帧中创建一个锁记录(Lock Record),并将对象头中的锁标志位指向这个锁记录。
- 重量级锁:如果锁竞争激烈,轻量级锁会升级为重量级锁,涉及到操作系统的互斥量(mutex)实现。重量级锁会导致线程阻塞和上下文切换。
5. 底层实现示例
以下是一个简单的示例,展示了同步代码块和同步方法在字节码层面的实现:
同步代码块
public void syncBlock() {
synchronized (this) {
System.out.println("Inside synchronized block");
}
}
编译后的字节码:
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 Inside synchronized 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
同步方法
public synchronized void syncMethod() {
System.out.println("Inside synchronized method");
}
编译后的字节码:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Inside synchronized method
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
总结
Synchronized
通过对象头中的锁标志位和Monitor对象实现了多线程的同步机制。通过锁的升级与膨胀策略,它在无竞争的情况下尽可能减少同步开销,而在竞争激烈的情况下保证线程安全。理解其底层原理有助于更好地使用 synchronized
,从而编写出高效的并发程序。