ReentrantLock公平非公平锁

wen java案例 1

ReentrantLock 的公平锁与非公平锁

ReentrantLock 是 Java 中重要的可重入互斥锁,它提供了两种锁模式:公平锁非公平锁

ReentrantLock公平非公平锁

核心区别

特性 非公平锁(默认) 公平锁
获取锁顺序 允许插队 严格按照FIFO队列顺序
性能 更高(减少线程唤醒开销) 较低(上下文切换更多)
避免饥饿 可能产生线程饥饿 不会饥饿
适用场景 大部分业务场景 对公平性有严格要求的场景

源码实现对比

非公平锁的实现

// NonfairSync.java
final void lock() {
    // 直接尝试抢占锁,不检查等待队列
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1); // 获取失败才进入队列
}
protected final boolean tryAcquire(int acquires) {
    // 调用父类的nonfairTryAcquire
    return nonfairTryAcquire(acquires);
}
// AbstractQueuedSynchronizer.java
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 即使有线程在等待队列中,也允许直接抢占
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 可重入逻辑...
}

公平锁的实现

// FairSync.java
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;
        }
    }
    // 可重入逻辑...
}

公平锁的判断方法

// 检查是否有其他线程在当前线程之前等待
public final boolean hasQueuedPredecessors() {
    Node t = tail;
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

这个方法返回 true 表示有排在当前线程前面的线程:

  • 队列不为空(h != t
  • 队列中第一个等待节点不是当前线程

性能差异分析

非公平锁为什么性能更高?

  1. 减少上下文切换:被唤醒的线程需要上下文切换,新到的线程如果直接获取锁,可以避免这次上下文切换
  2. 减少线程挂起/唤醒:非公平锁允许新线程插队,避免了部分线程进入等待队列

使用示例

public class ReentrantLockDemo {
    // 创建公平锁和非公平锁
    private static ReentrantLock fairLock = new ReentrantLock(true);
    private static ReentrantLock unfairLock = new ReentrantLock(false); // 默认
    public static void testLock(ReentrantLock lock, String lockType) {
        for (int i = 0; i < 3; i++) {
            int threadId = i;
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    lock.lock();
                    try {
                        System.out.println(lockType + " - 线程" + threadId + 
                                         " 第" + j + "次获取锁");
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }, "Thread-" + i).start();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== 公平锁测试 ===");
        testLock(fairLock, "公平锁");
        Thread.sleep(3000);
        System.out.println("\n=== 非公平锁测试 ===");
        testLock(unfairLock, "非公平锁");
    }
}

选择建议

  1. 默认使用非公平锁:大部分场景下,非公平锁的性能更好,能满足需求
  2. 使用公平锁的场景
    • 对锁获取顺序有严格要求的业务
    • 避免线程饥饿
    • 响应时间要求非常均匀
  3. 权衡考虑:公平锁虽然保证了公平性,但会牺牲一定的吞吐量

可以通过 lock.isFair() 方法判断当前锁的模式。

抱歉,评论功能暂时关闭!