Java 公平锁与非公平锁详解

Java 公平锁与非公平锁详解

一、基本概念

1. 公平锁 (Fair Lock)

定义 :多个线程按照申请锁的时间顺序获取锁,即"先来先得"原则

特点

  • 线程按请求锁的时间先后顺序排队
  • 新线程到来时,会检查等待队列
  • 如果队列中有等待线程,则新线程会加入队列尾部
  • 保障资源分配的绝对公平性

2. 非公平锁 (Non-fair Lock)

定义:允许线程"插队",新线程可直接尝试获取锁,而不一定遵循请求顺序

特点

  • 新线程可以尝试"插队"直接获取锁
  • 若获取失败才进入等待队列
  • 锁释放时,可能唤醒队列中线程,也可能被新来的线程抢占
  • 吞吐量通常比公平锁更高

二、工作流程对比

公平锁工作流程:

锁空闲? 空闲 队列空 队列非空 线程A请求锁 锁状态 检查等待队列 线程A获取锁 线程A进入等待队列 线程A执行 线程B唤醒获取锁

非公平锁工作流程:

直接尝试获取 是 否 锁释放时 成功 失败 线程A请求锁 获取成功? 线程A持有锁执行 进入等待队列 尝试与队列外线程竞争

三、Java中的实现与使用

1. ReentrantLock的公平/非公平控制

java 复制代码
// 创建非公平锁(默认)
ReentrantLock nonFairLock = new ReentrantLock(); 

// 创建公平锁
ReentrantLock fairLock = new ReentrantLock(true); 

// 使用示例
public void accessResource() {
    fairLock.lock();  // 公平获取锁
    try {
        // 临界区代码
    } finally {
        fairLock.unlock();  // 确保释放锁
    }
}

2. synchronized 与公平性

  • synchronized关键字实现的是非公平锁
  • 开发者无法控制synchronized的公平性
  • 需要公平锁时必须使用ReentrantLock

四、性能对比与适用场景

特性 公平锁 非公平锁
吞吐量 较低 较高
线程等待时间 相对平均 差异较大(可能饥饿)
实现复杂度 较高 相对简单
上下文切换 较多 较少
适用场景 1. 要求严格公平 2. 线程等待时间敏感 1. 高并发场景 2. 吞吐量优先

适用场景分析:

推荐公平锁的场景

  1. 要求严格顺序执行的业务(如交易处理系统)
  2. 线程等待时间至关重要的情况
  3. 避免线程饥饿(如资源分配系统)

推荐非公平锁的场景

  1. 高并发应用(最大吞吐量优先)
  2. 锁持有时间短且操作频繁
  3. 减少线程切换开销的场景

五、底层实现原理

1. 公平锁实现机制

java 复制代码
// ReentrantLock的公平获取锁逻辑
protected final boolean tryAcquire(int acquires) {
    // 检查是否有前驱节点在等待
    if (getFirstQueuedThread() != Thread.currentThread() && 
        hasQueuedPredecessors()) {
        return false; // 队列中有等待线程,排队
    }
    // ...其他逻辑
}

2. 非公平锁实现机制

java 复制代码
// 非公平获取锁的典型逻辑
final void lock() {
    // 首先尝试直接获取锁(插队)
    if (compareAndSetState(0, 1)) 
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1); // 失败后再进入队列
}

3. CLH队列(锁的等待队列)

两种锁类型都使用CLH变体的FIFO等待队列:

  • 线程加入队列时自旋检查前驱节点状态
  • 当前驱节点释放锁时通知后续节点

六、实战示例

公平锁实例:

java 复制代码
public class FairLockExample {
    private final ReentrantLock lock = new ReentrantLock(true);
    
    public void performTask() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 获取锁");
            // 模拟操作
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    // 测试:多个线程将按启动顺序获取锁
}

非公平锁实例:

java 复制代码
public class NonFairLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void performTask() {
        // 尝试插队获取锁
        if (lock.tryLock()) {
            try {
                System.out.println(Thread.currentThread().getName() + " 插队成功");
                Thread.sleep(50);
            } finally {
                lock.unlock();
            }
        } else {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 排队获取");
                Thread.sleep(100);
            } finally {
                lock.unlock();
            }
        }
    }
}

七、如何选择合适的锁

决策因素

  1. 吞吐量需求:高并发场景优先非公平锁
  2. 公平性要求:交易系统优先公平锁
  3. 锁持有时间
    • 短时间操作:非公平锁
    • 长时间操作:公平锁(减少饥饿)
  4. 系统资源
    • CPU资源有限:非公平锁(减少切换)
    • 内存资源充足:公平锁

默认建议 :大多数场景下非公平锁是更好的选择,因为:

  • Java中synchronized默认就是非公平
  • ReentrantLock默认实现也是非公平
  • 提供更好的整体吞吐量

八、总结

对比维度 公平锁 非公平锁
排队机制 严格遵守FIFO 允许插队
吞吐量 较低 较高
响应时间 可预测 不确定
实现难度 较复杂 较简单
避免饥饿 不能

核心结论

  1. 公平锁保障绝对的请求顺序
  2. 非公平锁提供更高的吞吐量
  3. Java中锁的默认行为是非公平的(符合大多数场景)
  4. 在需要严格顺序的业务中才使用公平锁
相关推荐
这个DBA有点耶8 小时前
数据库一体机架构演进:从硬件堆叠到软硬深度耦合
服务器·网络·数据库·硬件架构·运维开发·database·数据库架构
arbboter9 小时前
【网络工具】NetProxy网络代理用户手册
开发语言·网络·c#·网络代理·网络异常·代理工具
极创信息9 小时前
系统安全隐患全方位排查方案:标准化渗透测试全流程
java·opencv·struts·数据挖掘·eclipse·语音识别·hibernate
汉字萌萌哒10 小时前
2019CCF-CSP入门级C++试题解析
java·开发语言·c++
喜欢的名字被抢了10 小时前
程序出问题怎么查,以及如何让它不掉线
java·运维·数据库
消失的旧时光-194310 小时前
第一篇:Ktor Client 到底是什么?从 Retrofit 迁移理解 Ktor 网络请求架构
网络·架构·retrofit·ktor·dsl
数据知道11 小时前
反序列化漏洞:Java、PHP、Python 三条线各讲透
java·网络·python·安全·网络安全·php
江畔柳前堤11 小时前
Function Calling 与 Tool Calling:从认知到工程的全景深度解析
开发语言·网络·人工智能·深度学习·算法·机器学习·php
2601_9499506311 小时前
Java 面试准备,用练题簿把“背过八股”变成“真正会答”
java·开发语言·面试·刷题·小程序推荐
LabVIEW开发11 小时前
LabVIEW 通过以太网控制 Yokogawa WT3000 功率分析仪:tmctl.dll 与原始套接字方案
网络·labview·labview知识·labview功能·labview程序