You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Java LinkedBlockingQueue源码中锁与条件变量赋值逻辑的疑问

LinkedBlockingQueue中把全局锁/变量赋值给局部变量的原因

在阅读LinkedBlockingQueue源码时,你可能会注意到:明明takeLock、putLock、count这些成员变量已经是类中定义的final字段,每次在方法里使用时却还要先赋值给局部变量。这么做主要有以下几个原因:

1. 性能优化:降低字段访问开销

类的成员字段存储在堆内存中,每次访问都需要通过this引用间接寻址。而局部变量存储在栈上,甚至可以被JIT编译器缓存到CPU寄存器中,访问速度远快于堆上的成员字段。

对于lock()、unlock()这种高频调用的方法,把全局锁引用赋值给局部变量后,后续的锁操作都直接使用栈上的引用,能减少重复的堆内存寻址开销,累积下来对高并发场景下的性能提升有帮助。

2. 确保引用稳定性,避免潜在优化问题

虽然final字段在初始化完成后能保证对其他线程的可见性,但将其赋值给局部变量后,相当于在当前线程的栈中固定了这个引用。这样可以避免JIT编译器在优化时可能出现的指令重排序,确保整个方法执行期间,我们操作的始终是同一个锁/变量对象,不会出现意外的引用变化。

3. 编码约定与可读性

源码注释里也明确提到:Note: convention in all put/take/etc is to preset local var,这是LinkedBlockingQueue乃至JUC包中的一种编码惯例。把核心变量提前赋值给局部变量,能让代码风格保持一致,后续阅读代码时不用反复跳转查看类成员定义,逻辑更清晰,同时减少this.关键字的重复书写。


锁与条件变量初始化位置

/** The capacity bound, or Integer.MAX_VALUE if none */
private final int capacity;

/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();

/**
 * Head of linked list.
 * Invariant: head.item == null
 */
transient Node<E> head;

/**
 * Tail of linked list.
 * Invariant: last.next == null
 */
private transient Node<E> last;

/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();

/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();

/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();

/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();

赋值并使用的示例

// example 1
private void signalNotEmpty() {
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
}

// example 2
public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    // Note: convention in all put/take/etc is to preset local var
    // holding count negative to indicate failure unless set.
    int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();
    try {
        /*
         * Note that count is used in wait guard even though it is
         * not protected by lock. This works because count can
         * only decrease at this point (all other puts are shut
         * out by lock), and we (or some other waiting put) are
         * signalled if it ever changes from capacity. Similarly
         * for all other uses of count in other wait guards.
         */
        while (count.get() == capacity) {
            notFull.await();
        }
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}

内容的提问来源于stack exchange,提问作者PopCornnnn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 09:57:28