跳转至

ReentrantLock 如何设置公平锁

一、构造函数

public ReentrantLock() {
    sync = new NonfairSync();   // 默认非公平
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

二、公平锁的 tryAcquire

static final class FairSync extends Sync {
    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;
            }
        } else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0) throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

hasQueuedPredecessors()

public final boolean hasQueuedPredecessors() {
    Node h = head;
    Node s;
    return h != null &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

意思是:队列里有人在我前面排队,我就不抢。

三、公平 vs 非公平对比

公平 非公平
lock() 直接 acquire 先 CAS 抢
tryAcquire 检查 hasQueuedPredecessors 不检查
新线程行为 排队 直接抢
性能
饥饿 不会 可能

四、为什么默认非公平

  • 线程挂起/唤醒有开销(微秒级)。
  • 非公平让新线程在队列线程唤醒间隙抢锁,减少切换。
  • 大多数场景吞吐优先。

五、什么时候用公平

  • 要求严格 FIFO。
  • 持有锁时间长,队列等待明显。
  • 对公平性要求高于性能。

注意

公平锁不保证线程按调用顺序拿锁——如果队列里第一个线程被中断,它会退出队列,第二个线程顶上。