Study guide

13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`

Oracle Java SE 17 Developer Certified Professional 1Z0-829

Track your progress as you read

Finished sections get a check mark.

1 Introduction: The 1Z0-829 Exam

About the Java SE 17 Developer (1Z0-829) Exam

2 Chapter 1: Java Fundamentals & Language Basics

1.1 Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting 1.2 Local Variable Type Inference (`var`) Rules and Scoping 1.3 Operators, Precedence, Evaluation Order, and Short-Circuit Evaluation 1.4 Text Blocks, String Immutability, and StringBuilder APIs

3 Chapter 2: Program Flow & Decision Constructs

2.1 Conditional Branching with if/else and the Ternary Operator 2.2 Switch Statements and Switch Expressions 2.3 Iteration Constructs, Loop Control, and Branching Labels

4 Chapter 3: Object-Oriented Design & Class Mechanics

3.1 Class Declarations, Field/Method Signatures, and Access Modifiers 3.2 Constructors, Overloading, Initialization Order, and Garbage Collection 3.3 Inheritance, Method Overriding, Polymorphism, and `super`/`this` 3.4 Abstract Classes, Static Members, and Object Methods (`equals`, `hashCode`, `toString`)

5 Chapter 4: Advanced OOP: Interfaces, Records & Sealed Types

4.1 Interface Architecture: Default, Static, and Private Methods 4.2 Enumerations: Constants, Constructors, Methods, and Custom Bodies 4.3 Records: Canonical/Compact Constructors, Components, and Immutability 4.4 Sealed Classes and Interfaces: `permits`, Hierarchy Constraints, and Modifiers

6 Chapter 5: Pattern Matching & Nested Types

5.1 Pattern Matching for `instanceof` and Flow Scoping Mechanics 5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes 5.3 Defensive Programming, Immutability Patterns, and Validation

7 Chapter 6: Generics, Collections & Arrays

6.1 Generic Classes, Interfaces, Methods, and Type Erasure 6.2 Bounded Type Parameters and Wildcards (PECS Rule) 6.3 Collections Framework: List, Set, Queue, and Deque APIs 6.4 The Map Interface, Sorting, Comparable, and Comparator 6.5 Arrays: Declaration, Initialization, Multidimensional Arrays, and the Arrays Utility Class

8 Chapter 7: Functional Programming & Lambda Expressions

7.1 Functional Interfaces and Lambda Syntax Rules 7.2 Standard Functional Interfaces in `java.util.function` 7.3 Method References: Static, Bound, Unbound, and Constructor References

9 Chapter 8: The Stream API

8.1 Stream Lifecycle: Creation, Pipeline Architecture, and Lazy Evaluation 8.2 Intermediate Stream Operations: Filtering, Mapping, Sorting, and Slicing 8.3 Terminal Stream Operations: Reduction, Matching, Finding, and Collecting 8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics

10 Chapter 9: Advanced Streams & Collectors

9.1 The Optional Class: Safe Null Handling, Transformation, and Chaining 9.2 The Collectors API: Downstream Reductions, Grouping, and Partitioning 9.3 Parallel Streams: Fork/Join Pool, Spliterators, and Concurrency Pitfalls

11 Chapter 10: Exception Handling

10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors 10.2 Exception Constructs: `try-catch-finally`, Multi-Catch, and Rethrowing 10.3 Try-With-Resources, `AutoCloseable`, Suppressed Exceptions, and Custom Exceptions

12 Chapter 11: Date/Time API & Localization

11.1 Date and Time Core Types: `LocalDate`, `LocalTime`, `LocalDateTime` 11.2 Timestamps, Intervals, and Durations: `Instant`, `Period`, and `Duration` 11.3 Time Zones, Daylight Saving Time (DST) Offsets, and `ZonedDateTime` 11.4 Internationalization and Localization: `Locale`, `ResourceBundle`, and Formatters 11.5 Formatting and Parsing Messages, Numbers, Percentages, and Localized Dates

13 Chapter 12: Java Platform Module System (JPMS)

12.1 Module Architecture, `module-info.java`, and Module Paths 12.2 Module Directives: `requires`, `requires transitive`, `exports`, and Qualified Exports 12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules 12.4 Services in JPMS (`uses`, `provides...with`), Migration Paths, and Tooling (`jlink`, `jdeps`) 12.5 Compiling and Packaging Code: Non-Modular JARs, Modular JARs, and Migration Strategies

14 Chapter 13: Concurrency & Multithreading

13.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle 13.2 Memory Visibility, `volatile`, Intrinsic Locks, and Synchronization 13.3 The Concurrency Framework: `ExecutorService`, Thread Pools, and `Future` 13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`

15 Chapter 14: Java I/O and NIO.2

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering 14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID` 14.3 NIO.2 Architecture: `Path`, `Paths`, and Basic `Files` Operations 14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`

16 Chapter 15: Accessing Databases with JDBC

15.1 JDBC Architecture, Drivers, `DriverManager`, and `DataSource` Connections 15.2 SQL Execution: `Statement`, `PreparedStatement`, and `CallableStatement` 15.3 `ResultSet` Navigation, Data Types, and Transaction Management (Commit, Rollback, Savepoints)

17 Chapter 16: Annotations & the Java Logging API

16.1 Standard Annotations: @Override, @FunctionalInterface, @Deprecated, @SuppressWarnings, and @SafeVarargs 16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration

13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`

Key Takeaways Explain This Topic AI

Atomic variables in java.util.concurrent.atomic employ hardware-level Compare-And-Swap (CAS) instructions to provide thread-safe, lock-free mutations.

ConcurrentHashMap provides high-concurrency read and write operations via bucket-level locking without throwing ConcurrentModificationException, unlike synchronized map wrappers that lock the entire table.

CopyOnWriteArrayList creates a new internal copy of the backing array on every write, providing immutable snapshot iterators ideal for read-heavy, low-mutation scenarios.

ReentrantLock provides explicit, interruptible, timed, and fair lock acquisition capabilities that exceed intrinsic monitors, requiring mandatory lock() outside and unlock() inside a finally block.

CompletableFuture enables reactive, non-blocking asynchronous pipelines, supporting transformation (thenApply), composition (thenCompose), combination (thenCombine), and robust fallback error handling (exceptionally, handle).

Last updated: October 2026

Concurrent Collections, Atomic Variables, Explicit Locks, and CompletableFuture

While intrinsic synchronization ( synchronized ) provides simple mutual exclusion, high-throughput applications require specialized concurrent primitives to minimize thread contention and maximize multi-core hardware saturation. Java SE 17 provides lock-free atomic variables, highly optimized concurrent collections, explicit lock abstractions, and composable asynchronous completion stages.

Lock-Free Atomic Variables and Compare-And-Swap (CAS)

The java.util.concurrent.atomic package provides classes such as AtomicInteger , AtomicLong , AtomicBoolean , and AtomicReference<V> . These classes support thread-safe operations on single variables without utilizing intrinsic locks.

Compare-And-Swap (CAS) Mechanics

Instead of blocking threads, atomic classes utilize CPU hardware instructions known as Compare-And-Swap (e.g., cmpxchg on x86 architectures). A CAS instruction operates on three operands:

A memory location ( V V V ).

The expected old value ( A A A ).

The new value to write ( B B B ).

The processor atomically updates V V V to B B B if and only if the current value at V V V matches A A A . If the value has changed in the interim, the operation fails, and the calling thread loops to retry the operation (optimistic retry loop): AtomicInteger counter = new AtomicInteger(0);

// Atomic increment:

int nextVal = counter.incrementAndGet(); // Returns 1

// Conditional CAS update:

boolean updated = counter.compareAndSet(1, 10); // Sets to 10 if current is 1

// Atomic functional transformation:

int transformed = counter.updateAndGet(x -> x * 2); // 10 * 2 = 20

CAS eliminates thread suspension, context-switch overhead, and priority inversion, providing significantly higher throughput under low-to-moderate contention than intrinsic locks.

Concurrent Collections vs. Synchronized Wrappers

Legacy collections can be synchronized using factory methods such as Collections.synchronizedMap() or Collections.synchronizedList() .

The Problem with Synchronized Wrappers

Single Mutex Bottleneck : Synchronized wrappers wrap the underlying collection with a single, coarse-grained mutex lock. Every read and write operation contends for the same lock, serializing all access.

Unsafe Iteration : Iterators over synchronized wrappers are not thread-safe unless the client code manually synchronizes on the collection wrapper during the entire iteration loop. Failure to do so risks ConcurrentModificationException . Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());

// MUST manually synchronize on syncMap during iteration:

  • synchronized (syncMap) {
  • for (String key : syncMap.keySet()) { /* ... */ }
  • }
  • High-Performance Concurrent Collections

Java provides modern concurrent collections designed from the ground up for multi-threaded workloads:

Collection Concurrency Mechanism Iteration Behavior Ideal Use Case

ConcurrentHashMap Bucket-level locks and CAS operations Weakly consistent; never throws ConcurrentModificationException General-purpose, high-concurrency key-value store

CopyOnWriteArrayList Clones backing array on every mutation Snapshot view; cannot modify during iteration Read-heavy, rarely modified lists (e.g., event listeners)

ConcurrentLinkedQueue Lock-free queue using CAS pointers Weakly consistent High-throughput, non-blocking FIFO queue

ArrayBlockingQueue Bounded array with explicit locks Weakly consistent Fixed-capacity producer-consumer pipelines

LinkedBlockingQueue Optionally bounded linked nodes Weakly consistent Variable-capacity producer-consumer pipelines

Atomic Operations in ConcurrentHashMap

ConcurrentHashMap provides atomic compound methods that avoid external synchronization:

putIfAbsent(K, V) : Inserts the value only if the key is not currently mapped.

computeIfAbsent(K, Function) : Computes and inserts a value atomically if the key is absent.

computeIfPresent(K, BiFunction) : Recomputes a mapping if the key is present.

merge(K, V, BiFunction) : Merges an existing value with a new value atomically.

CopyOnWriteArrayList Semantics

In a CopyOnWriteArrayList , all mutative operations ( add , set , remove ) create a fresh copy of the internal backing array. Readers iterate over an immutable snapshot taken when the iterator was constructed. Iterators over a CopyOnWriteArrayList do not support remove() ; calling iterator.remove() throws UnsupportedOperationException .

Explicit Locks: ReentrantLock and ReentrantReadWriteLock

The java.util.concurrent.locks package provides explicit locking interfaces that offer advanced capabilities beyond intrinsic monitor blocks.

ReentrantLock Standard Idiom

Unlike synchronized blocks, explicit locks require manual acquisition and release. The call to lock() must occur immediately before the try block , and unlock() must reside in the finally block: Lock lock = new ReentrantLock();

lock.lock(); // Acquire BEFORE try!

  • try {
  • // Critical section
  • } finally {
  • lock.unlock(); // Ensure lock is ALWAYS released
  • }
  • Capabilities Beyond Intrinsic Locks

Non-Blocking Lock Acquisition : lock.tryLock() attempts to acquire the lock immediately without waiting, returning true if successful and false otherwise.

Timed Lock Acquisition : lock.tryLock(500, TimeUnit.MILLISECONDS) waits up to the timeout before giving up, breaking deadlock hazards.

Interruptible Acquisition : lock.lockInterruptibly() acquires the lock unless the current thread is interrupted, responding promptly to cancellation.

Fairness Policies : new ReentrantLock(true) creates a fair lock that grants access in FIFO order to the longest-waiting thread, eliminating thread starvation at the expense of throughput.

ReentrantReadWriteLock

Optimizes scenarios where read operations substantially outnumber write operations:

Read Lock ( lock.readLock() ) : Multiple threads can hold the read lock simultaneously, provided no thread holds the write lock.

Write Lock ( lock.writeLock() ) : Mutually exclusive. Only one thread can hold the write lock, barring all other readers and writers.

Lock Downgrading vs. Upgrading : Holding the write lock and acquiring the read lock before releasing the write lock ( downgrading ) is fully supported. However, acquiring the write lock while holding a read lock ( upgrading ) is not supported and triggers deadlock .

Asynchronous Orchestration with CompletableFuture

Introduced in Java 8 and enhanced in Java 9 through 17, CompletableFuture<T> implements both Future<T> and CompletionStage<T> . It enables non-blocking, declarative, event-driven pipelines. [supplyAsync] ---> .thenApply() ---> .thenCombine() ---> .exceptionally()

  • (Fetch Data) (Transform) (Join Another) (Fallback)
  • 1. Initiating Asynchronous Pipelines

CompletableFuture.supplyAsync(Supplier<U>) : Executes the supplier asynchronously on ForkJoinPool.commonPool() and returns CompletableFuture<U> .

CompletableFuture.runAsync(Runnable) : Executes a Runnable asynchronously, returning CompletableFuture<Void> .

Both methods offer overloads accepting a custom Executor .

2. Chaining Stages (Transformations & Callbacks)

thenApply(Function<T, R>) : Synchronously transforms the result of the previous stage into a new type R .

thenAccept(Consumer<T>) : Consumes the output of the stage, returning CompletableFuture<Void> .

thenRun(Runnable) : Runs an action after the prior stage completes without consuming its output.

Async Variants ( thenApplyAsync , etc.) : Execute the dependent stage on a different thread using ForkJoinPool.commonPool() or a designated Executor .

3. Composing and Combining Futures

thenCompose(Function<T, CompletableFuture<R>>) : Flattens nested futures (analogous to flatMap ), chaining dependent asynchronous calls.

thenCombine(CompletionStage<U>, BiFunction<T, U, V>) : Runs two independent stages in parallel and combines their results with a BiFunction when both complete.

CompletableFuture.allOf(CompletableFuture<?>... cfs) : Returns a future that completes when all input futures complete.

CompletableFuture.anyOf(CompletableFuture<?>... cfs) : Returns a future that completes when any of the input futures completes.

4. Resilient Exception Handling

exceptionally(Function<Throwable, T>) : Catches an unhandled exception thrown anywhere up the pipeline and returns a fallback recovery value.

handle(BiFunction<T, Throwable, R>) : Always executes upon stage completion, receiving either the computed result or the thrown Throwable .