Study guide

9.4 Concurrent Collections, Atomics, and Explicit Locks

Oracle Java SE 21 Developer 1Z0-830

Track your progress as you read

Finished sections get a check mark.

1 Chapter 1: Java Basics, Primitive Types, and Operators

1.1 Program Structure, Primitives, Literals, Scopes, and var 1.2 Operators, Precedence, and Type Casting 1.3 Strings, Text Blocks, and StringBuilder 1.4 Date and Time APIs (java.time)

2 Chapter 2: Controlling Program Flow & Pattern Matching

2.1 Conditional Logic: if-else and Ternary Operators 2.2 Switch Statements and Switch Expressions 2.3 Pattern Matching for switch and instanceof 2.4 Loops and Branching Statements

3 Chapter 3: Object-Oriented Design & Class Architecture

3.1 Class Declarations, Constructors, and Object Lifecycle 3.2 Encapsulation and Access Modifiers 3.3 Inheritance, Method Overriding, and Polymorphism 3.4 Abstract Classes and Interfaces 3.5 Nested, Inner, Local, and Anonymous Classes 3.6 Method Overloading, Overload Resolution, and Varargs

4 Chapter 4: Modern Java Data Modeling: Records, Enums, and Sealed Types

4.1 Java Records, Canonical/Compact Constructors, and Component Accessors 4.2 Java Enums: Fields, Methods, and Abstract Constant-Specific Behavior 4.3 Sealed Classes and Interfaces: permits, final, non-sealed, and Hierarchy Architecture 4.4 Advanced Record Patterns (JEP 440), Nested Deconstruction, and Sealed Hierarchy Exhaustiveness

5 Chapter 5: Exception Handling and Annotations

5.1 Exception Hierarchy: Checked vs. Unchecked Exceptions 5.2 Exception Handling: try, catch, finally, and Multi-Catch 5.3 Resource Management: try-with-resources and AutoCloseable 5.4 Custom Exceptions and Best Practices 5.5 Java Annotations: Built-in, Meta-Annotations, and Custom Types

6 Chapter 6: Generics and the Collections Framework

6.1 Generics Fundamentals and Generic Methods 6.2 Wildcards, Type Bounds, and Type Erasure 6.3 Core Collections: Lists, Sets, Queues, and Maps 6.4 Sequenced Collections in Java 21 6.5 Sorting, Comparable, and Comparator 6.6 Arrays: Creation, Multidimensional Forms, and the Arrays Utility Class

7 Chapter 7: Functional Programming & Lambda Expressions

7.1 Built-in Functional Interfaces in java.util.function 7.2 Lambda Expressions, Variable Scopes, and Effectively Final 7.3 Method References: Four Varieties and Edge Cases 7.4 Working with java.util.Optional

8 Chapter 8: The Stream API and Data Pipelines

8.1 Stream Creation and Intermediate Operations 8.2 Terminal Operations and Reductions 8.3 Advanced Collectors, Grouping, Partitioning, and Teeing 8.4 Primitive Streams and Parallel Stream Processing

9 Chapter 9: Concurrency, Multithreading, and Virtual Threads

9.1 Thread Fundamentals, Creation, and Lifecycle 9.2 Thread Safety, Synchronization, and the Java Memory Model 9.3 Concurrency Utilities: ExecutorService, Callable, and Future 9.4 Concurrent Collections, Atomics, and Explicit Locks 9.5 Virtual Threads in Java 21 (Carrier Threads, Pinning, and Best Practices)

10 Chapter 10: The Java Platform Module System (JPMS)

10.1 Module Architecture and module-info.java Declarations 10.2 Module Directives: requires, exports, opens, provides, and uses 10.3 Modular Compilation, Packaging, and CLI Execution 10.4 Module Types, Migration Strategies, ServiceLoader SPI, and JLink

11 Chapter 11: Java I/O, NIO.2, and Object Serialization

11.1 Classic Java I/O Streams and Readers/Writers 11.2 Java NIO.2 File API: Path, Paths, and File System Operations 11.3 Advanced NIO.2 Stream Operations and Directory Traversal 11.4 Java Object Serialization and Record Serialization

12 Chapter 12: Implementing Localization and Internationalization

12.1 Locales and Resource Bundles 12.2 Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages

9.4 Concurrent Collections, Atomics, and Explicit Locks

Key Takeaways Explain This Topic AI

ConcurrentHashMap provides high-throughput thread safety using bucket-level fine-grained locking and CAS, strictly forbids null keys and values, and yields weakly consistent iterators that never throw ConcurrentModificationException.

CopyOnWriteArrayList creates a fresh backing array upon every mutation, providing immutable snapshot iterators whose remove() method throws UnsupportedOperationException.

Atomic classes in java.util.concurrent.atomic leverage low-level Compare-And-Swap (CAS) CPU instructions to perform lock-free, thread-safe mutations on single variables.

ReentrantLock offers explicit locking with tryLock() timeouts, condition variables, and fairness options, while ReentrantReadWriteLock permits multiple concurrent readers but only a single exclusive writer.

ReentrantReadWriteLock supports lock downgrading from write lock to read lock, but attempting to upgrade a read lock to a write lock causes a deadlock.

Last updated: September 2026

Concurrent Collections, Atomics, and Explicit Locks

High-performance concurrent applications require specialized data structures and synchronization primitives that avoid coarse-grained monitor bottlenecks. The java.util.concurrent package provides lock-free collections, copy-on-write collections, atomic variable wrappers, explicit locks, and thread coordination synchronizers.

1. Concurrent Collections vs. Synchronized Wrappers

Legacy synchronized collections ( Collections.synchronizedMap() , Vector , Hashtable ) synchronize every operation on a single, coarse-grained object monitor. This serializes all operations, creating severe contention under high concurrency, and still requires external synchronization when iterating.

In contrast, modern concurrent collections use lock striping , Compare-And-Swap (CAS) operations, and copy-on-write arrays to enable concurrent reads and writes without global locks.

  • 2. Core Concurrent Collection Implementations
  • A. ConcurrentHashMap<K, V>

Lock Granularity : Employs fine-grained locking on individual hash bucket bins using synchronized blocks and CAS operations. Reads are entirely non-blocking.

Null Policy : Strictly forbids null keys and null values . Attempting to insert a null key or null value throws a NullPointerException ! (Unlike standard HashMap which permits one null key and multiple null values).

Atomic Compound Operations :

putIfAbsent(key, value) : Inserts value only if key is not already mapped.

computeIfAbsent(key, mappingFunction) : Computes and inserts value atomically if key is absent.

computeIfPresent(key, remappingFunction) : Updates value atomically if key is present.

merge(key, value, remappingFunction) : Combines old and new values atomically.

Weakly Consistent Iterators : Iterators reflect the map state at or after iterator creation, will never throw ConcurrentModificationException , and can tolerate concurrent modifications. ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();

// map.put(null, 1); // THROWS NullPointerException!

// map.put("KEY", null); // THROWS NullPointerException!

  • // Thread-safe atomic counter increment
  • map.merge("visitors", 1, Integer::sum);
  • B. ConcurrentSkipListMap<K, V> and ConcurrentSkipListSet<E>

Structure : A thread-safe, scalable concurrent sorted map based on a probabilistic SkipList data structure.

Ordering : Maintains elements in natural key order or according to a provided Comparator .

Performance : Provides O ( log ⁡ n ) O(\log n) O ( lo g n ) time cost for containsKey , get , put , and remove .

Null Policy : Strictly forbids null keys and null values (throws NullPointerException ).

C. CopyOnWriteArrayList<E> and CopyOnWriteArraySet<E>

Design : Every mutative operation ( add() , set() , remove() ) allocates a brand new copy of the underlying backing array.

Workload Fit : Highly optimized for read-heavy, write-rare scenarios (e.g., event listener lists, routing tables).

Snapshot Iterators :

Iterators operate on an immutable snapshot of the backing array captured at the instant the iterator was created.

Concurrent modifications made to the list after iterator creation do not affect the iteration snapshot.

ConcurrentModificationException is never thrown .

Iterator Mutations Forbidden : Invoking iterator.remove() , iterator.add() , or iterator.set() throws UnsupportedOperationException ! List<String> list = new CopyOnWriteArrayList<>(List.of("Alpha", "Beta"));

  • for (String item : list) {
  • list.add("Gamma"); // Modifies list, but does not affect the active iterator snapshot

// iterator.remove(); // THROWS UnsupportedOperationException!

  • }
  • System.out.println(list); // Prints: [Alpha, Beta, Gamma, Gamma]
  • D. BlockingQueue<E> Implementations & Operations Matrix

BlockingQueue implementations coordinate producer-consumer processing pipelines by blocking threads when attempting to insert into a full queue or retrieve from an empty queue.

  • Queue Action Throws Exception Special Value Blocks Indefinitely Blocks with Timeout
  • Insert add(e) (throws IllegalStateException ) offer(e) (returns false ) put(e) offer(e, timeout, unit)
  • Remove remove() (throws NoSuchElementException ) poll() (returns null ) take() poll(timeout, unit)
  • Examine element() (throws NoSuchElementException ) peek() (returns null ) N/A N/A

Common Implementations:

ArrayBlockingQueue : Bounded array-backed FIFO queue.

LinkedBlockingQueue : Optionally bounded node-linked FIFO queue.

PriorityBlockingQueue : Unbounded priority heap queue.

SynchronousQueue : Zero-capacity direct handoff queue (each insert must wait for a concurrent take).

3. Atomic Variables and Compare-And-Swap (CAS)

The java.util.concurrent.atomic package ( AtomicInteger , AtomicLong , AtomicBoolean , AtomicReference<V> ) provides lock-free, thread-safe single-variable mutations.

Hardware Compare-And-Swap (CAS)

CAS is a low-level CPU instruction that performs an optimistic read-modify-write: CAS ( V , expectedValue , newValue ) \text{CAS}(V, \text{expectedValue}, \text{newValue}) CAS ( V , expectedValue , newValue )

If memory location V V V still contains expectedValue \text{expectedValue} expectedValue , it updates V V V to newValue \text{newValue} newValue and returns true . If another thread modified V V V in the interim, CAS returns false and the operation loops (spins) and retries without suspending the thread. AtomicInteger counter = new AtomicInteger(0);

  • int next = counter.incrementAndGet(); // Atomic ++counter
  • int prev = counter.getAndIncrement(); // Atomic counter++
  • // Atomic conditional update
  • boolean success = counter.compareAndSet(2, 10); // If value is 2, sets to 10
  • // Atomic functional updates
  • counter.updateAndGet(x -> x * 2);
  • counter.accumulateAndGet(5, (x, y) -> x + y);
  • AtomicReference<V>
  • Used for atomic updates on custom immutable domain objects: record UserConfig(String theme, int fontSize) {}
  • AtomicReference<UserConfig> configRef = new AtomicReference<>(new UserConfig("DARK", 14));
  • configRef.updateAndGet(current -> new UserConfig("LIGHT", current.fontSize()));
  • High-Contention Counters: LongAdder vs. AtomicLong

Under heavy multi-thread write contention, threads competing on a single AtomicLong spend excessive CPU cycles spinning on CAS retries. LongAdder distributes updates across an internal array of striped cell counters and sums them on sum() , eliminating contention bottlenecks.

  • 4. Explicit Locks ( java.util.concurrent.locks )
  • ReentrantLock

ReentrantLock implements the Lock interface, providing capabilities beyond intrinsic synchronized monitors: Lock lock = new ReentrantLock(true); // 'true' enables FIFO fair acquisition ordering

  • lock.lock(); // Blocks until lock is acquired
  • try {
  • // Critical Section
  • } finally {

lock.unlock(); // ALWAYS unlock in a finally block!

}

Advanced ReentrantLock Capabilities:

Non-Blocking Lock Acquisition ( tryLock() ) : if (lock.tryLock()) { try { ... } finally { lock.unlock(); } } prevents deadlocks.

Timed Lock Acquisition ( tryLock(timeout, unit) ) : Waits for lock up to a specified deadline.

Interruptible Acquisition ( lockInterruptibly() ) : Responds immediately to Thread.interrupt() while waiting.

Condition Variables ( lock.newCondition() ) : Replaces wait() / notify() with multiple independent wait-sets per lock ( await() , signal() , signalAll() ).

ReentrantReadWriteLock

Maintains a pair of associated locks: a shared read lock and an exclusive write lock :

Multiple threads can hold the read lock simultaneously when no write lock is held.

Only one thread can hold the write lock (exclusive).

Lock Downgrading IS Permitted : Hold write lock → \rightarrow → acquire read lock → \rightarrow → release write lock.

Lock Upgrading is NOT Permitted : Holding a read lock and attempting to acquire a write lock causes a permanent deadlock ! ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

  • Lock rLock = rwLock.readLock();
  • Lock wLock = rwLock.writeLock();
  • // Lock Downgrading Example (Legal)
  • wLock.lock();
  • try {

// Modify shared state...

  • rLock.lock(); // Acquire read lock while holding write lock (Downgrade)
  • } finally {
  • wLock.unlock(); // Release write lock; still hold read lock
  • }
  • try {

// Read shared state...

  • } finally {
  • rLock.unlock();
  • }
  • 5. Thread Coordination Synchronizers

Synchronizer Operational Model Reusability

CountDownLatch(int count) Blocks threads at await() until countDown() is called N N N times. One-time use (count cannot be reset)

CyclicBarrier(int parties, Runnable action) Blocks N N N threads until all N N N arrive at await() . Executes barrier action, then releases all threads. Reusable (automatically resets for next cycle)

Semaphore(int permits) Restricts access to a finite pool of N N N shared resources via acquire() and release() . Reusable permits pool

CountDownLatch Example (One-Way Gate) CountDownLatch latch = new CountDownLatch(3);

  • for (int i = 0; i < 3; i++) {
  • new Thread(() -> {
  • System.out.println("Service initialized");
  • latch.countDown(); // Decrements count by 1
  • }).start();
  • }
  • latch.await(); // Main thread blocks until count reaches 0
  • System.out.println("All services initialized. System startup complete.");

CyclicBarrier Example (Multi-Phase Synchronization) CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("=== Phase Completed ==="));

  • Runnable worker = () -> {
  • try {
  • System.out.println("Phase 1 computation");
  • barrier.await(); // Waits for all 3 threads, triggers action, resets barrier
  • System.out.println("Phase 2 computation");

barrier.await(); // Reusable for phase 2!

  • } catch (Exception e) {}
  • };

Loading diagram... CountDownLatch vs. CyclicBarrier Synchronization Models