1. synchronized
是非公平锁吗?
是的,synchronized
是非公平锁。
什么是非公平锁?
非公平锁意味着线程获取锁的顺序并不一定按照先后到达的顺序,也就是说,后来的线程有可能插队,优先获取锁。这种机制可以减少线程切换的开销,提高系统的吞吐量。
如何体现 synchronized
的非公平性?
sychronized
的非公平性体现在JVM 的内部实现上。当多个线程竞争锁时,synchronized
并不会按照线程请求锁的顺序来安排锁的获取,而是让竞争最激烈的线程尽快获取锁。这种设计有助于避免某些线程长期得不到锁的情况,也能加快线程获取锁的速度。
2. 锁能降级吗?
在 synchronized
机制下,锁不能降级。
什么是锁降级?
锁降级是指将持有的高等级锁(如写锁)降级为低等级锁(如读锁)的过程。常见于可重入锁(如 ReentrantReadWriteLock
)中,降级可以通过以下过程实现:
- 持有写锁时,如果希望多个线程进行只读访问,先获取读锁再释放写锁。
- 降级允许系统在保持一定并发性能的同时,提供必要的线程安全性。
但在 synchronized
机制中,锁的持有要么是独占锁(写锁),要么没有持有,不支持类似读写锁的降级行为。
3. synchronized
的实现机制
synchronized
是 JVM 内置的锁机制,基于对象头的 MarkWord
字段,通过偏向锁、轻量级锁和重量级锁来实现对锁的优化。
- 偏向锁:当只有一个线程竞争时,线程偏向锁住对象,几乎无性能损耗。
- 轻量级锁:当多个线程开始竞争时,会升级为轻量级锁,通过自旋等待的方式减少上下文切换。
- 重量级锁:当自旋等待的时间过长或线程数量增多时,锁升级为重量级锁,涉及线程阻塞和唤醒机制,性能相对较差。
在 synchronized
中,锁升级是可以的(从偏向锁到重量级锁),但锁降级是不可用的。
4. 锁的非公平性和实现的设计考量
非公平锁在高并发情况下可以提升系统的吞吐量。synchronized
采用非公平锁的原因是:
- 避免线程饥饿:如果采用公平锁,会导致后续线程等待锁的时间较长,增加了线程切换的成本。
- 提高吞吐量:非公平锁允许后来的线程有机会获取锁,减少了线程排队等待的时间,提高了锁的获取速度和系统的整体效率。
5. 如何检测 synchronized
的非公平性
可以通过设计一个多线程竞争锁的场景来观察:
public class SynchronizedFairnessTest {
private static int counter = 0;
public synchronized static void increment() {
counter++;
System.out.println(Thread.currentThread().getName() + " incremented counter to: " + counter);
}
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
increment();
try {
Thread.sleep(50); // 模拟其他操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// 创建多个线程,模拟并发
Thread t1 = new Thread(task, "Thread 1");
Thread t2 = new Thread(task, "Thread 2");
Thread t3 = new Thread(task, "Thread 3");
t1.start();
t2.start();
t3.start();
}
}
在多线程的环境下,Thread 1
、Thread 2
和 Thread 3
的执行顺序是非确定性的,因为 synchronized
不保证公平性。
6. 业务场景和问题解决
在高并发业务场景下,比如订单处理系统中多个线程同时处理订单生成操作时,synchronized
非公平锁可以提高吞吐量,让高并发的请求迅速得到处理。但在某些对顺序要求严格的场景下(如日志系统、数据审计),这种非公平性可能会带来问题。因此,在需要严格控制线程获取锁顺序的场景中,可以考虑使用 ReentrantLock
并设置其为公平锁。