Backend Development 21 min read

High‑Frequency Java Concurrency Questions: AQS, Locks, Thread Pools, Blocking Queues, CountDownLatch, Semaphore, CopyOnWriteArrayList, and ConcurrentHashMap

This article explains the core concepts and common pitfalls of Java's AbstractQueuedSynchronizer (AQS) and its derived utilities such as ReentrantLock, ReentrantReadWriteLock, CountDownLatch, Semaphore, as well as the design and behavior of blocking queues, thread‑pool parameters, CopyOnWriteArrayList, and ConcurrentHashMap, providing code examples and practical guidance.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
High‑Frequency Java Concurrency Questions: AQS, Locks, Thread Pools, Blocking Queues, CountDownLatch, Semaphore, CopyOnWriteArrayList, and ConcurrentHashMap

1. AQS Frequently Asked Questions

1.1 What is AQS?

AQS (AbstractQueuedSynchronizer) is the foundation of many JUC utilities, including lock implementations, CountDownLatch , Semaphore , thread pools, etc. It maintains a volatile state field updated via CAS, a doubly‑linked list for waiting threads, and a singly‑linked list for condition queues.

1.2 Why does AQS traverse the list from tail to head when waking threads?

When a thread releases a resource, it must locate a node to wake. Traversing from the tail avoids missing nodes that might have been cancelled, preventing node‑loss problems during concurrent additions.

1.3 Why does AQS use a doubly‑linked list instead of a singly‑linked list?

Cancellation of nodes requires only two pointer updates in a doubly‑linked list (prev.next and next.prev), whereas a singly‑linked list would need a full traversal, wasting resources.

1.4 Why does AQS have a dummy head node?

The dummy head (sentinel) simplifies state handling for each node, which can represent various statuses such as cancelled (1), default (0), successor suspended (-1), in condition queue (-2), or shared lock propagation (-3).

1.5 How is ReentrantLock implemented?

Lock acquisition attempts to CAS state from 0 to 1; success means the lock is acquired.

If CAS fails, the thread is enqueued in AQS's doubly‑linked list and may be suspended.

When a thread calls await on a condition, it is moved to the condition’s singly‑linked list until signalled.

1.6 Fair vs. non‑fair lock differences

Non‑fair lock : lock() tries to set state directly; if it fails, tryAcquire() is invoked.

Fair lock : lock() always delegates to tryAcquire() , which first checks the queue; only the head thread may acquire the lock.

Both eventually enqueue the thread in AQS if acquisition fails.

1.7 How does ReentrantReadWriteLock work?

It uses the 32‑bit state field: the low 16 bits represent the write lock, the high 16 bits represent the read lock count. Write‑lock re‑entrancy is tracked directly in the low bits; read‑lock re‑entrancy uses a ThreadLocal in addition to the high bits.

2. Blocking Queue Frequently Asked Questions

2.1 Common blocking queues

ArrayBlockingQueue : array‑backed, fixed capacity.

LinkedBlockingQueue : linked‑list backed, optionally bounded.

PriorityBlockingQueue : array‑backed binary heap, unbounded (expands).

Both ArrayBlockingQueue and LinkedBlockingQueue are the most used queues in ThreadPoolExecutor .

2.2 What is a spurious wake‑up?

When a consumer thread awaits an empty queue, a producer may add an element and signal the consumer. If another consumer acquires the lock first, the first consumer may wake up without an element, causing a spurious wake‑up. The fix is to use a while loop for the condition check.

3. Thread‑Pool Frequently Asked Questions

3.1 Seven thread‑pool parameters

Core pool size, maximum pool size, keep‑alive time, time unit, work queue, thread factory, and rejection policy.

3.2 Thread‑pool states

The pool can be in RUNNING , SHUTDOWN , STOP , TIDYING , or TERMINATED , recorded in the internal ctl integer.

3.3 Common rejection policies

AbortPolicy : discard task and throw RejectedExecutionException (default).

CallerRunsPolicy : the calling thread runs the task.

DiscardPolicy : silently discard the task.

DiscardOldestPolicy : discard the oldest queued task and retry.

3.4 Thread‑pool execution flow

Core threads are created lazily when tasks are submitted; they fetch tasks from the queue using take() . Non‑core threads use poll() with a timeout and terminate after being idle.

3.5 Why add idle non‑core threads?

When the core pool size is zero, tasks would otherwise sit in the queue with no workers. Idle non‑core threads are created to consume queued tasks.

3.6 What happens when a worker thread throws an exception?

If the task was submitted via execute() , the exception propagates.

If submitted via submit() , the exception is captured in the associated FutureTask and can be retrieved with Future.get() .

The worker thread then terminates.

3.7 Why do workers extend AQS?

Workers use AQS state to cooperate with shutdown() (interrupt idle workers) and shutdownNow() (interrupt all workers).

3.8 How to set core parameters?

For CPU‑bound tasks, a common rule is CPU cores + 1 . For I/O‑bound tasks, benchmark to keep CPU utilization around 70‑80%.

4. CountDownLatch and Semaphore Frequently Asked Questions

4.1 What is CountDownLatch and how does it work?

It is a counter based on AQS. The constructor sets state to the initial count. Each countDown() decrements the state; when it reaches zero, all waiting threads are released.

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch count = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(() -> {
                try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
                System.out.println("Thread " + finalI + " running");
                count.countDown();
            }).start();
        }
        count.await();
        System.out.println("All threads finished");
    }
}

4.2 What is Semaphore and how does it work?

It limits concurrent access by maintaining a permit count in state . acquire() decrements the state; if the state is zero, the thread blocks. release() increments the state, potentially waking a waiting thread.

import java.util.concurrent.Semaphore;

public class SemaphoreTest {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println("Thread " + finalI + " running");
                    Thread.sleep(5000);
                } catch (InterruptedException e) { e.printStackTrace(); }
                finally { semaphore.release(); }
            }).start();
        }
        new Thread(() -> {
            try {
                long begin = System.currentTimeMillis();
                semaphore.acquire();
                long end = System.currentTimeMillis();
                System.out.println("Rate‑limited " + (end - begin) + " ms");
            } catch (InterruptedException e) { e.printStackTrace(); }
            finally { semaphore.release(); }
        }).start();
    }
}

4.3 Does the program stop when the main thread ends?

If non‑daemon worker threads are still running, the JVM stays alive.

If only daemon threads remain, the JVM exits.

5. CopyOnWriteArrayList Frequently Asked Questions

5.1 How does CopyOnWriteArrayList guarantee thread safety and what are its drawbacks?

Writes acquire a ReentrantLock , copy the underlying array, modify the copy, and then replace the reference, ensuring readers see a consistent snapshot. The drawback is high memory and CPU cost for large data sets because each write creates a new array.

6. ConcurrentHashMap (JDK 1.8) Frequently Asked Questions

6.1 Why is HashMap not thread‑safe?

Concurrent modifications can create cycles during resize.

Updates may overwrite each other, causing data loss.

Size counters become inaccurate under concurrency.

Data migration during resize can lose entries.

6.2 How does ConcurrentHashMap achieve thread safety?

Insertion into the bucket array uses CAS.

Updates to linked lists or tree bins use synchronized blocks.

Counting uses a LongAdder -like mechanism based on CAS.

Resize operations are coordinated with CAS on the sizeCtl field and double‑checked locking.

6.3 When and how does ConcurrentHashMap resize?

When the number of entries exceeds capacity * loadFactor (0.75) .

Calling putAll() with many entries may trigger immediate resize.

If the table length is less than 64 and a bin length reaches 8, a resize is also initiated.

6.4 Why is the load factor 0.75 and why convert a bin to a red‑black tree at length 8?

0.75 balances space utilization and collision probability. Poisson‑distribution analysis shows that a bin length of 8 occurs with probability ~0.00000006, so converting to a tree is rare and only worthwhile when collisions become frequent.

6.5 Does heavy put activity block during resize?

Usually not. Threads encountering a migrating bin help with the resize and then retry insertion, avoiding long pauses.

6.6 How are counters implemented?

They use a LongAdder -style striped counter: each thread updates a cell via CAS, and the sum of cells provides the total count.

6.7 Are read operations blocked?

No. Reads inspect the array, linked list, tree, or migrated bin without acquiring locks; even during resize, threads can safely read from the new table.

7. Summary

This guide consolidates common, high‑frequency questions about Java’s concurrency primitives—AQS, various lock implementations, thread‑pool configuration, blocking queues, synchronization aids, and the internal mechanics of CopyOnWriteArrayList and ConcurrentHashMap —providing concise explanations, code snippets, and best‑practice recommendations for developers working on backend systems.

JavaconcurrencythreadpoolConcurrentHashMapLockAQSBlockingQueue
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login 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.