2

I am reading Java LinkedBlockingQueue source code, and I have two questions about the put operation.

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();
}

I don't understand:

  1. It checks c + 1 < capacity, when it emits notFull.signal(). Why not use c < capacity?
  2. Why it emits signalNotEmpty when c == 0 at the end of function?

Besides, I also don't understand why head/tail need to be transient?

Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
GarudaReiga
  • 1,057
  • 2
  • 9
  • 12

1 Answers1

4

The answers to your two questions:

  1. Before the c + 1 < capacity check, there's a call to c = count.getAndIncrement();. Since the count is incremented after it's retrieved and assigned to c, there's a difference of 1 between c and the actual count, hence the + 1 in the check.
  2. The not-empty signal only needs to be sent when the queue transitions from being empty to being not-empty. This is only the case when the first element is added to an empty (0 elements) queue. As to c at this point being 0 instead of 1, refer to the answer to the first question.

About the head and tail being transient: they don't need to be persisted upon object serialization, because they're initialized through the default constructor upon object deserialization.

Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
  • 1
    Thanks, now I understand the trick for getAndIncrement(). The head/tail are pointing to node with null value in the default constructor. Does it mean that if we serialize/deserialize the blocking queue, we lose the content in the blocking queue? – GarudaReiga Nov 06 '14 at 06:32
  • The `head` is always an empty node in this implementation. You won't lose anything. – Robby Cornelissen Nov 06 '14 at 06:50