Why Does ArrayBlockingQueue’s Constructor Use a Lock? Uncovering Visibility and Reordering

This article explains why the ArrayBlockingQueue constructor acquires a lock, detailing how the lock ensures visibility of the internal array, prevents instruction‑reordering hazards during object initialization, and avoids cache‑consistency problems in multithreaded Java applications.

Programmer DD
Programmer DD
Programmer DD
Why Does ArrayBlockingQueue’s Constructor Use a Lock? Uncovering Visibility and Reordering

Yesterday a colleague asked why a constructor in ArrayBlockingQueue needs to acquire a lock. The answer lies in ensuring visibility of the internal items array and preventing instruction‑reordering issues during object creation.

First, consider the constructor implementation:

public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) {
    this(capacity, fair);
    final ReentrantLock lock = this.lock;
    lock.lock(); // Lock only for visibility, not mutual exclusion
    try {
        int i = 0;
        for (E e : c) {
            checkNotNull(e);
            items[i++] = e;
        }
        count = i;
        putIndex = (i == capacity) ? 0 : i;
    } finally {
        lock.unlock();
    }
}

The fifth line obtains the mutex, but its purpose is not to provide mutual exclusion; it guarantees that writes to items become visible to other threads. Without the lock, the Java Memory Model allows the write to the array reference and the array’s contents to be reordered, leading to a situation where another thread sees a partially constructed queue.

Instruction reordering occurs because the compiler or runtime may rearrange operations for performance. Object creation involves three steps: allocating memory, initializing the object, and assigning the reference. Reordering steps 2 and 3 can cause another thread to observe a reference to an uninitialized array.

Consider two threads: Thread A constructs the queue, while Thread B performs enqueue/dequeue operations. If Thread A’s initialization steps are reordered, Thread B may access items before it is fully initialized, causing data races.

Another perspective is cache consistency. Modern CPUs use caches to bridge the speed gap between processor and main memory. Without synchronization, each thread may keep its own cached copy of items, and updates made by one thread might never be flushed to main memory, breaking consistency.

All explanations above reflect personal understanding; corrections are welcome.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyMemory ModelLockArrayBlockingQueue
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.