
1. 公平锁与非公平锁的本质区别公平锁Fair Lock和非公平锁Nonfair Lock是并发编程中两种不同的锁获取策略它们的核心差异体现在线程获取锁的排队机制上。理解这个区别需要先了解锁的底层实现原理。以Java的ReentrantLock为例其内部通过AQSAbstractQueuedSynchronizer实现锁的获取与释放。当多个线程竞争锁时公平锁会严格维护一个FIFO先进先出的等待队列新来的线程必须排队等候非公平锁允许新来的线程直接尝试获取锁无需立即进入等待队列这种差异直接反映在锁的tryAcquire()方法实现上。在ReentrantLock的源码中公平锁的实现会先检查hasQueuedPredecessors()protected final boolean tryAcquire(int acquires) { final Thread current Thread.currentThread(); int c getState(); if (c 0) { if (!hasQueuedPredecessors() // 关键区别检查是否有等待线程 compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } // ...重入逻辑 }而非公平锁的实现会直接尝试CAS操作获取锁final boolean nonfairTryAcquire(int acquires) { final Thread current Thread.currentThread(); int c getState(); if (c 0) { if (compareAndSetState(0, acquires)) { // 直接尝试获取 setExclusiveOwnerThread(current); return true; } } // ...重入逻辑 }2. 性能特征与线程饥饿问题2.1 吞吐量对比非公平锁的吞吐量通常比公平锁高20%~30%这个差异主要来自线程切换成本公平锁强制线程挂起和唤醒涉及内核态切换CPU缓存利用率新线程可能仍持有CPU缓存直接执行更高效临界区执行时间当锁持有时间较短时非公平锁的优势更明显实测案例在16核机器上对100万次锁获取操作进行基准测试锁类型耗时(ms)吞吐量(ops/ms)公平锁1250800非公平锁95010522.2 线程饥饿风险非公平锁可能导致低优先级线程长期无法获取锁。典型场景是存在热线程频繁获取/释放锁锁持有时间非常短100μs系统负载较高线程竞争激烈这种情况下新来的线程可能总是比等待队列中的线程先获取锁。解决方案包括使用公平锁设置线程优先级但Java的线程优先级在Linux上映射有限实现带权重的锁获取策略如StampedLock3. 典型使用场景分析3.1 适合公平锁的场景交易系统需要保证请求处理的顺序性股票交易订单处理银行转账操作需要严格先来后到的业务逻辑资源分配系统数据库连接池分配线程池任务调度打印机等物理设备访问防止优先级反转实时系统中高优先级线程不能被低优先级线程阻塞3.2 适合非公平锁的场景高性能计算计数器递增缓存更新统计信息收集短暂临界区状态标志修改简单对象赋值耗时1ms的操作读多写少场景结合读写锁ReentrantReadWriteLock配置读锁为非公平模式4. 实战配置与注意事项4.1 ReentrantLock的配置方式// 公平锁 ReentrantLock fairLock new ReentrantLock(true); // 非公平锁默认 ReentrantLock nonfairLock new ReentrantLock();4.2 性能调优建议临界区耗时100μs优先考虑非公平锁线程竞争激烈8个线程测试公平锁的实际影响需要顺序保证必须使用公平锁混合场景可考虑分段锁策略4.3 常见问题排查问题1非公平锁下某些线程长期无法获取锁检查锁持有时间是否过长考虑引入tryLock()带超时机制监控线程状态JStack/JMX问题2公平锁性能不达预期检查是否有线程在临界区内阻塞如IO操作评估是否真的需要严格顺序考虑改用非公平锁队列外部的顺序控制5. 扩展应用模式5.1 混合策略实现可以结合两种锁的优点实现动态策略class HybridLock { private final ReentrantLock lock new ReentrantLock(false); private final AtomicInteger waitCount new AtomicInteger(); void lock() { if (waitCount.get() THRESHOLD) { new ReentrantLock(true).lock(); // 切换为公平模式 } else { lock.lock(); } waitCount.incrementAndGet(); } void unlock() { lock.unlock(); waitCount.decrementAndGet(); } }5.2 与其它并发工具配合与Condition配合公平锁的Condition队列也是公平的非公平锁的signal()唤醒顺序不确定在线程池中的应用FixedThreadPool适合公平锁CachedThreadPool适合非公平锁注意线程池大小与锁类型的匹配在实际项目中我曾遇到一个支付系统因错误使用非公平锁导致订单处理顺序错乱的问题。通过将核心交易路径改为公平锁同时保持对账等后台任务使用非公平锁最终实现了吞吐量和顺序性的平衡。关键是要根据具体业务需求进行选择没有绝对的好坏之分。