Overview
Domain 8 has three objectives. The first covers creating platform and virtual threads, using Runnable and Callable, managing the thread lifecycle, and running tasks through the different ExecutorService types and the concurrent API. The second covers writing thread-safe code with synchronized blocks, Lock implementations, atomic classes and other java.util.concurrent tools. The third covers processing collections concurrently and using parallel streams.
Virtual threads, finalized in Java 21, are the headline addition compared with the Java 17 exam. Expect to create them with Thread.ofVirtual, Thread.startVirtualThread or Executors.newVirtualThreadPerTaskExecutor, and to know how they differ from platform threads. Most questions show short code using executors, futures or shared counters and ask for output or behaviour.
How it's tested
Expect snippets that start threads or submit tasks and print results, along with questions about whether output is fixed. Read for start versus run, shutdown and close calls, the return type of submit, and whether shared state is protected. When the honest answer is that output varies, Oracle usually offers that as an option.
-
Deterministic or not
Several threads update shared data, and you decide whether the printed total is fixed or could vary between runs.
-
Executor behaviour
Tasks are submitted with submit, invokeAll or schedule, and you choose what get returns, when the executor stops, or which exception appears.
-
Virtual thread facts
A snippet creates virtual threads, and you identify their daemon status, how to start them, or how they compare with pooled platform threads.
Two threads each run a loop that increments a shared int field 1,000 times with count++, and the main thread joins both before printing. Many expect 2000. count++ is a read, add and write sequence, so updates can interleave and be lost, and the printed value can be 2000 or lower. Replacing the field with an AtomicInteger and calling incrementAndGet would guarantee 2000.
Objectives 3
Official objectives from the Oracle outline, in outline order.
8.1 Create both platform and virtual threads. Use both Runnable and Callable objects, manage the thread lifecycle, and use different Executor services and concurrent API to run tasks.Thread creation, task types, thread states, executors, futures and scheduled tasks.
- What it means
- A thread runs a task concurrently with other threads. Platform threads wrap operating system threads, while virtual threads are lightweight threads scheduled by the JVM onto a small pool of carrier threads. Tasks are written as Runnable, which returns nothing, or Callable, which returns a value and may throw checked exceptions. Executor services manage threads for you and return Future objects.
- What to understand
- Create platform threads with new Thread(r), Thread.ofPlatform() or executors, and virtual threads with Thread.ofVirtual().start(r), Thread.startVirtualThread(r) or Executors.newVirtualThreadPerTaskExecutor(). Virtual threads are always daemon threads, so the JVM can exit without waiting for them, and they are meant to be created per task rather than pooled. Calling start launches a new thread, calling run executes on the current thread, and starting a thread twice throws IllegalThreadStateException. States are NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING and TERMINATED, and join waits for completion. submit accepts Runnable or Callable and returns a Future; get blocks and wraps task exceptions in ExecutionException. Know invokeAll, invokeAny, shutdown versus shutdownNow, awaitTermination, close, and ScheduledExecutorService with schedule, scheduleAtFixedRate and scheduleWithFixedDelay.
- How it shows up
- Expect code using virtual thread builders, Future.get results, start versus run, and executors closed in try-with-resources.
- Common confusion
- Thinking virtual threads should be pooled like platform threads, or that shutdown stops tasks already running.
8.2 Develop thread-safe code, using locking mechanisms and concurrent API.Race conditions, synchronized, Lock, atomics, coordination classes and liveness problems.
- What it means
- Thread-safe code produces correct results when several threads access shared data. Java provides intrinsic locks through synchronized methods and blocks, explicit locks such as ReentrantLock and ReentrantReadWriteLock, atomic classes such as AtomicInteger and AtomicLong, and coordination classes such as CountDownLatch and CyclicBarrier.
- What to understand
- A synchronized instance method locks on this, a static synchronized method locks on the Class object, and a synchronized block locks on the object you name, so two threads only exclude each other when they use the same lock. ReentrantLock requires an explicit unlock, normally in a finally block; tryLock returns immediately with a boolean, and unlocking a lock you do not hold throws IllegalMonitorStateException. Atomic classes offer methods such as incrementAndGet, getAndIncrement, compareAndSet and updateAndGet. volatile guarantees visibility of writes but not atomicity of compound actions such as count++. Recognise deadlock, where threads wait on each other's locks forever, starvation, and livelock. In Java 21, a virtual thread that blocks while holding a synchronized lock pins its carrier thread, which is one reason ReentrantLock is often suggested for virtual-thread code.
- How it shows up
- Expect shared counters with and without synchronization, Lock code missing unlock, and scenarios naming a liveness problem.
- Common confusion
- Believing volatile makes count++ safe, or that synchronizing on different objects still prevents interference.
8.3 Process Java collections concurrently and utilize parallel streams.Concurrent collections, ConcurrentModificationException and parallel stream behaviour.
- What it means
- Standard collections such as ArrayList and HashMap are not safe for concurrent modification. The java.util.concurrent package supplies thread-safe alternatives, including ConcurrentHashMap, CopyOnWriteArrayList, ConcurrentLinkedQueue and blocking queues such as LinkedBlockingQueue. Parallel streams split work across the common ForkJoinPool.
- What to understand
- Modifying an ArrayList while iterating it, even on one thread, typically throws ConcurrentModificationException, while CopyOnWriteArrayList iterates over a snapshot and never throws it. ConcurrentHashMap supports atomic compound operations such as merge, compute and putIfAbsent, and rejects null keys and values. Collections.synchronizedList wraps a list with locking, but iteration still needs manual synchronization. A parallel stream may process elements in any order, so forEach output varies, while collect and reduce with an associative operation and neutral identity give consistent results. Lambdas that add to a shared ArrayList from a parallel stream are unsafe; use a collector instead.
- How it shows up
- Expect CopyOnWriteArrayList iteration output, ConcurrentHashMap merge results and parallel streams writing to shared lists.
- Common confusion
- Thinking Collections.synchronizedList makes iteration safe without an explicit lock.
High-yield concepts
The terms, rules and formulas this domain turns into questions most often.
-
Virtual thread
Lightweight JVM-scheduled thread, standard since Java 21.
- Remember
- Always daemon; create one per task.
- Tested as
- Creation and behaviour questions.
- Trap
- Putting virtual threads in a fixed pool.
-
start versus run
start creates a new thread; run is a plain method call.
- Remember
- Calling start twice throws IllegalThreadStateException.
- Tested as
- Output and thread-name questions.
- Trap
- Expecting run to execute concurrently.
-
Callable
Task that returns a value and may throw checked exceptions.
- Remember
- Submitted with submit or invokeAll.
- Tested as
- Future result questions.
- Trap
- Passing it to new Thread directly.
-
Future.get
Blocks until a result is ready.
- Remember
- Task failures arrive as ExecutionException.
- Tested as
- Exception and blocking questions.
- Trap
- Expecting the original exception type to be thrown.
-
ExecutorService.close
AutoCloseable since Java 19.
- Remember
- Waits for submitted tasks to finish.
- Tested as
- try-with-resources executors.
- Trap
- Thinking executors cannot be used in try headers.
-
shutdown versus shutdownNow
Orderly stop versus attempted interruption.
- Remember
- shutdown lets queued tasks finish.
- Tested as
- Lifecycle questions.
- Trap
- Expecting shutdown to cancel running work.
-
ReentrantLock
Explicit lock with tryLock and fairness options.
- Remember
- Always unlock in finally.
- Tested as
- Lock usage questions.
- Trap
- Forgetting unlock and blocking other threads.
-
AtomicInteger
Lock-free thread-safe integer.
- Remember
- incrementAndGet returns the new value; getAndIncrement the old.
- Tested as
- Shared counter questions.
- Trap
- Mixing up the return values.
-
volatile
Visibility guarantee for a field.
- Remember
- Not atomic for read-modify-write.
- Tested as
- Race condition questions.
- Trap
- Using it to fix count++.
-
ConcurrentHashMap
Thread-safe map with atomic compound methods.
- Remember
- Rejects null keys and values.
- Tested as
- Concurrent update questions.
- Trap
- Expecting null support as in HashMap.
-
CopyOnWriteArrayList
List that copies on every write.
- Remember
- Iterators see a snapshot and never throw ConcurrentModificationException.
- Tested as
- Iteration-while-modifying questions.
- Trap
- Expecting new elements to appear during iteration.
-
Deadlock
Threads waiting on each other's locks forever.
- Remember
- Consistent lock ordering prevents it.
- Tested as
- Liveness scenario questions.
- Trap
- Confusing it with livelock, where threads stay active.
Side by side
Concepts candidates mix up, compared on the point that decides the answer.
Platform and virtual threads
| Concept | Meaning | Key difference | Common trap |
|---|---|---|---|
| Platform thread | Wrapper over an OS thread | Costly; suits CPU-bound work and pooling | Creating millions of them |
| Virtual thread | JVM-managed thread on carrier threads | Cheap; suits blocking I/O tasks; always daemon | Pooling them or calling setDaemon(false) |
| newVirtualThreadPerTaskExecutor | Executor creating one virtual thread per task | No pool size to tune | Expecting a fixed number of threads |
Thread-safety tools
| Concept | Meaning | Key difference | Common trap |
|---|---|---|---|
| synchronized | Intrinsic lock on an object or class | Automatic release on exit | Locking on different objects |
| ReentrantLock | Explicit lock object | tryLock, timeouts and fairness | Missing unlock in finally |
| Atomic classes | Lock-free single-variable updates | compareAndSet and increment methods | Using them for multi-variable invariants |
| volatile | Visibility of writes across threads | No mutual exclusion | Assuming count++ becomes atomic |
Exam traps
Each trap is an answer that looks right. Learn the clue that should change your mind.
-
Fixed total
- What candidates pick
- Picks the exact expected count for threads using count++.
- Why it looks right
- Each thread performs the right number of increments.
- Why it's wrong
- count++ is not atomic, so concurrent updates can be lost and the result can vary.
Clue to watch for A shared int field updated without synchronization or atomics.
-
run is concurrent
- What candidates pick
- Assumes calling run prints from a new thread.
- Why it looks right
- run contains the thread's code.
- Why it's wrong
- Only start creates a new thread; calling run executes synchronously on the caller's thread.
Clue to watch for thread.run() in place of thread.start().
-
Virtual threads keep JVM alive
- What candidates pick
- Expects the program to wait for an unjoined virtual thread to finish.
- Why it looks right
- Platform threads created with new Thread are non-daemon by default.
- Why it's wrong
- Virtual threads are always daemon, so main can finish and the JVM can exit before they print.
Clue to watch for A virtual thread started without join or an executor close.
-
volatile fixes races
- What candidates pick
- Chooses volatile to make a shared counter correct.
- Why it looks right
- It is the concurrency keyword for fields.
- Why it's wrong
- volatile ensures visibility only; the read-modify-write of count++ still interleaves without a lock or atomic class.
Clue to watch for A volatile int incremented by several threads.
-
shutdown cancels tasks
- What candidates pick
- Expects queued tasks to be discarded after shutdown.
- Why it looks right
- The name suggests stopping immediately.
- Why it's wrong
- shutdown refuses new tasks but lets submitted ones finish; shutdownNow attempts to interrupt and returns waiting tasks.
Clue to watch for Tasks submitted before a shutdown call.
-
synchronizedList iteration
- What candidates pick
- Assumes iterating a synchronized wrapper is safe without extra locking.
- Why it looks right
- Every method on the wrapper is synchronized.
- Why it's wrong
- Iteration spans many calls, so you must hold the list's lock manually during the loop.
Clue to watch for A for-each over a Collections.synchronizedList result.
Answer framework
Use this sequence to judge what concurrent code will actually do.
- Identify how each task starts: start, run, an executor submit, a virtual thread builder, or a parallel stream, and which thread executes it.
- Locate every piece of shared mutable state and check whether it is protected by the same lock, an atomic class or a concurrent collection.
- Decide whether ordering is enforced; if threads interleave freely and nothing enforces order, prefer the option saying output cannot be determined.
- Check lifecycle calls such as join, Future.get, shutdown, close and awaitTermination, plus daemon status, to see whether main waits for work to finish.
- Account for exceptions: ExecutionException from get, IllegalThreadStateException from double start, and ConcurrentModificationException from unsafe iteration.
Practice question
Want more like this? The 1Z0-830 practice test has questions for every domain.
Checklist
0 of 12
Tick each one you can do without notes. Your ticks stay in this browser.
Study strategy
Start with the lifecycle. Write a program that creates a platform thread and a virtual thread, prints their names and isVirtual values, and compares start with run. Then submit Runnable and Callable tasks to a fixed thread pool, a virtual-thread-per-task executor and a scheduled executor, using get, invokeAll and close in try-with-resources. Make one task throw so you see ExecutionException.
Next, break things on purpose. Increment a shared int from two threads many times and print the total several times; then fix it with synchronized, with ReentrantLock and with AtomicInteger, and compare. Mark the field volatile to show that alone does not fix the race.
Finally, cover collections and parallel streams. Modify an ArrayList during iteration, then repeat with CopyOnWriteArrayList. Use ConcurrentHashMap.merge from several tasks. Run a parallel stream with forEach and forEachOrdered and compare the output. Keep a list of the questions where the right answer was that output cannot be determined, since recognising them is a skill in itself.
FAQ
How much of 1Z0-830 is Domain 8, Managing Concurrent Code Execution?
Oracle does not publish a weight for this section. It has three objectives, and we plan for about five questions, several involving virtual threads or executors.
Are virtual threads final in Java 21?
Yes. Virtual threads became a standard feature in Java 21 through JEP 444, so they are fully in scope for this exam.
Is structured concurrency on the exam?
StructuredTaskScope was a preview API in Java 21. Oracle exams target standard features, so concentrate on threads, executors and the established concurrent API.
Should I pool virtual threads?
No. Virtual threads are cheap, so the recommended approach is one per task, for example with Executors.newVirtualThreadPerTaskExecutor, rather than a fixed pool.
What does Future.get throw if the task fails?
It throws ExecutionException, with the task's original exception available through getCause. It can also throw InterruptedException if the waiting thread is interrupted.
Do I need to know the Fork/Join framework in depth?
The objectives do not name it. Know that parallel streams use the common ForkJoinPool and focus on correctness of parallel results.
Test your Domain 8 knowledge
You have reviewed platform and virtual threads, Runnable and Callable, executors, locks, atomics, concurrent collections and parallel streams. Check whether you can tell fixed output from timing-dependent output.
Keep going
- Training Training Built on your Weak areas
- Practice test Questions by domain, with explanations
- Mock exam Full length, timed like the real exam
- Readiness test Find your weakest domain in minutes
- Cheat sheet Key terms and formulas on one page
- Ask the 1Z0-830 AI tutor Get any concept explained your way