JVM 对 Java 的原生锁(如 synchronized)做了多项优化,以提高其性能和可扩展性。以下是一些主要的优化措施:
锁消除(Lock Elimination):
锁消除是指 JVM 在某些情况下可以完全消除不必要的锁操作。例如,当一个对象的锁只在单线程中使用,并且不会被其他线程访问时,JVM 可以确定这个锁是不必要的,从而将其消除。
通过字节码分析和逃逸分析,JVM 可以识别出这些情况并优化掉锁操作。
锁粗化(Lock Coarsening):
锁粗化是指将多个连续的加锁和解锁操作合并为一个更大的锁操作。这样可以减少锁的开销,提高性能。
例如,如果一个方法中有多个 synchronized 代码块,JVM 可以将这些代码块合并为一个更大的 synchronized 代码块。
适应性自旋(Adaptive Spinning):
自旋锁是一种乐观锁机制,它在尝试获取锁时会进行一段时间的自旋(即循环等待),而不是立即进入阻塞状态。如果锁很快就会被释放,自旋锁可以避免线程切换的开销。
适应性自旋是指 JVM 会根据历史信息动态调整自旋的时间。如果一个线程在过去成功地通过自旋获取了锁,那么在未来再次尝试获取锁时,JVM 会增加自旋的时间;反之,则减少自旋的时间。
偏向锁(Biased Locking):
偏向锁是针对没有竞争的情况进行的优化。当一个线程第一次获取某个对象的锁时,JVM 会将该锁偏向于这个线程。如果后续的锁请求都来自同一个线程,那么这个线程可以直接获取锁而不需要进行额外的同步操作。
偏向锁可以显著减少无竞争情况下的锁开销。
轻量级锁(Lightweight Locking):
轻量级锁是在多线程竞争不激烈的情况下的一种优化。当一个线程尝试获取一个未被锁定的对象时,JVM 会使用原子操作将锁标记为已锁定,并记录当前线程的标识。
如果另一个线程尝试获取同一个锁,JVM 会检测到锁已经被占用,然后进入膨胀状态,转换为重量级锁(即传统的互斥锁)。
锁膨胀(Lock Inflation):
当轻量级锁无法处理多线程竞争时,JVM 会将锁膨胀为重量级锁。重量级锁使用操作系统提供的互斥锁机制,可以处理高并发情况下的锁竞争。
锁膨胀是一个平滑的过程,从轻量级锁逐渐过渡到重量级锁,以最小化性能影响。
示例代码
以下是一个简单的示例,展示了 synchronized 锁的使用:
▼java复制代码public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
SynchronizedExample example = new SynchronizedExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
example.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
example.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + example.getCount());
}
}
在这个示例中,increment 和 getCount 方法都是 synchronized 方法,确保了对 count 变量的线程安全操作。JVM 的优化措施可以帮助提高这些同步操作的性能。