Study guide
13.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle
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.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle
Key Takeaways Explain This Topic AI
Java platform threads map 1:1 to underlying operating system kernel threads; thread execution order and timeslicing are nondeterministically governed by the OS scheduler.
The Runnable functional interface defines 'void run()' and cannot throw checked exceptions, whereas Callable<V> defines 'V call() throws Exception' and returns a typed result.
Invoking start() transitions a thread from NEW to RUNNABLE and spawns a new OS execution thread, whereas invoking run() directly executes synchronously on the caller's thread without starting a new thread.
Calling start() more than once on the same Thread instance throws IllegalThreadStateException, as does calling setDaemon(true) after start() has been invoked.
The static Thread.interrupted() method returns the interrupt status of the current thread and clears it, while the instance method isInterrupted() queries the interrupt status without clearing the flag.
Last updated: October 2026
Thread Fundamentals, Runnable , Callable , and Thread Lifecycle
Concurrency enables multiple execution sequences to proceed simultaneously or interleaved across modern multi-core processors. In Java SE 17, understanding the boundary between operating system execution threads and Java runtime thread abstractions is fundamental. Mastering thread creation patterns, the exact state transitions of the thread lifecycle, daemon thread semantics, and thread coordination mechanics is essential for developing robust multi-threaded applications.
Operating System Threads vs. Java Platform Threads
In standard Java Virtual Machine (JVM) implementations, each instance of java.lang.Thread is a platform thread that wraps a 1:1 underlying operating system (OS) kernel thread . +-------------------------------------------------------------+
- | Java Application (JVM User Space) |
- | Thread A (Stack ~1MB) Thread B (Stack ~1MB) |
- +-------------------------------------------------------------+
- | |
- v (1:1 Native Mapping) v (1:1 Native Mapping)
- +-------------------------------------------------------------+
- | Operating System Kernel |
- | Kernel Thread 1042 Kernel Thread 1043 |
- | [Preemptive OS Scheduler / CPU Core Time-Slicing] |
- +-------------------------------------------------------------+
Because Java platform threads rely directly on native OS threads:
Resource Footprint : Each platform thread allocates a substantial memory footprint for its private call stack (typically 1 megabyte by default, configurable via the -Xss JVM flag).
Scheduling Mechanics : Thread scheduling is preemptive and platform-dependent. The underlying operating system scheduler decides which thread receives a CPU time slice (quantum) and when context switching occurs. Java code cannot guarantee execution ordering or execution velocity.
Thread Priorities : Java provides advisory thread priority levels from Thread.MIN_PRIORITY (1) to Thread.MAX_PRIORITY (10), defaulting to Thread.NORM_PRIORITY (5). However, operating systems map these values arbitrarily to native priority levels or ignore them altogether. Never rely on thread priorities for algorithmic correctness.
Creating Threads: Thread , Runnable , and Callable<V>
Java provides three primary mechanisms for expressing asynchronous or concurrent units of execution.
1. Extending java.lang.Thread
A class can extend Thread and override its public void run() method: public class WorkerThread extends Thread {
- @Override
- public void run() {
- System.out.println("Worker executing in: " + Thread.currentThread().getName());
- }
- }
// Instantiation and startup:
- WorkerThread worker = new WorkerThread();
- worker.start();
Extending Thread tightly couples the execution mechanism to the task logic and consumes the single class inheritance allowance in Java, making it the least flexible approach.
- 2. Implementing java.lang.Runnable
- The Runnable interface represents an abstract task decoupled from thread management: @FunctionalInterface
- public interface Runnable {
- void run();
- }
Because Runnable is a functional interface, it can be expressed concisely using lambda expressions or method references: Runnable task = () -> System.out.println("Task running in: " + Thread.currentThread().getName());
- Thread thread = new Thread(task, "CustomWorkerThread");
- thread.start();
Key constraints of Runnable :
The run() method returns void (it cannot return a computed result directly to the caller).
The run() method signature does not declare any checked exceptions; any checked exception must be caught and handled within the method body.
- 3. Implementing java.util.concurrent.Callable<V>
- Introduced in java.util.concurrent , Callable<V> overcomes the limitations of Runnable : @FunctionalInterface
- public interface Callable<V> {
- V call() throws Exception;
- }
Callable<V> returns a parameterized type V and can throw checked Exception instances. However, Thread does not possess a constructor accepting a Callable . Callable instances are submitted to an ExecutorService , which coordinates execution and returns a Future<V> .
- Feature Thread subclass Runnable Callable<V>
- Mechanism Class inheritance Functional interface Functional interface
- Return Type void void Generic type V
- Checked Exceptions Cannot throw Cannot throw Can throw Exception
- Execution Target Self-managed Passed to Thread or Executor Submitted to ExecutorService
- Thread Lifecycle and the Thread.State Enum
A Java thread resides in exactly one state at any given point in time, as defined by Thread.State : [NEW] ---(start())---> [RUNNABLE] <-----------------------+
- | |
- +-------------------+--------------------+ |
- | | | |
- v v v |
- [BLOCKED] [WAITING] [TIMED_WAITING] |
- (waiting for (wait(), join(), (sleep(), wait(t)|
- intrinsic monitor) park()) join(t)) |
- | | | |
- +-------------------+--------------------+---------+
- |
- v (run() completes)
- [TERMINATED]
NEW : The thread instance has been created ( new Thread(...) ), but start() has not yet been invoked.
RUNNABLE : The thread is executing in the JVM or is ready and waiting for resource allocation (CPU time slice) from the operating system scheduler.
BLOCKED : The thread is waiting to acquire an intrinsic monitor lock to enter a synchronized block or method, or to re-enter a synchronized block after Object.wait() .
WAITING : The thread is waiting indefinitely for another thread to perform a specific action without any timeout: Object.wait() , Thread.join() , or LockSupport.park() .
TIMED_WAITING : The thread is waiting for another thread to perform an action up to a specified waiting time: Thread.sleep(millis) , Object.wait(timeout) , Thread.join(timeout) , or LockSupport.parkNanos() .
TERMINATED : The thread has finished execution because its run() method completed normally or terminated abruptly due to an uncaught exception.
Warning
The IllegalThreadStateException Trap Once a thread transitions out of NEW by invoking start() , it can never be started again. Calling start() on an already started thread—even if it is currently in RUNNABLE , WAITING , or has already reached TERMINATED —immediately throws an IllegalThreadStateException .
The start() vs. run() Execution Trap
A frequent source of bugs and exam traps is the distinction between calling start() and calling run() directly: Thread thread = new Thread(() -> {
- System.out.println("Executing thread: " + Thread.currentThread().getName());
- });
thread.run(); // Executes SYNCHRONOUSLY on the CALLER thread (e.g., 'main')!
thread.start(); // Spawns a NEW native thread ('Thread-0') and executes asynchronously!
Calling run() directly : Invokes the run() method as an ordinary method call on the current thread . No new native thread is created, no OS scheduler registration occurs, and the caller blocks until run() returns.
Calling start() : Requests the JVM to allocate a native thread stack, registers the thread with the OS scheduler, transitions the thread state to RUNNABLE , and asynchronously invokes the run() method on the newly created thread.
Daemon Threads and JVM Termination Lifecycle
Java threads are divided into two categories:
User Threads (Non-Daemon) : High-priority threads that execute core application logic. The JVM remains active as long as at least one non-daemon thread is alive.
Daemon Threads : Background service providers (such as garbage collection or internal monitoring). They do not prevent the JVM from exiting. Thread daemonWorker = new Thread(() -> {
- try {
- while (true) {
- Thread.sleep(1000);
- System.out.println("Heartbeat...");
- }
- } finally {
System.out.println("Daemon finally block executing..."); // MAY NEVER RUN!
- }
- });
daemonWorker.setDaemon(true); // MUST be called BEFORE start()!
- daemonWorker.start();
- Critical Daemon Thread Rules
State Configuration Order : setDaemon(true) must be called before start() . Invoking setDaemon() on an active thread throws IllegalThreadStateException .
Inheritance : A newly created thread inherits the daemon status of the thread that created it.
Abrupt Termination : When all user (non-daemon) threads terminate, the JVM immediately terminates. Any running daemon threads are halted abruptly: their finally blocks do not execute , and resources held by them may not be released cleanly.
Thread Interruption and Coordination: interrupt() , join() , and sleep()
Java relies on a cooperative interruption policy . One thread cannot forcefully terminate another thread; it can only request that the target thread interrupt its current activity.
Interruption Mechanics and API Methods
Each thread maintains an internal boolean interrupt status flag :
t.interrupt() : Sets the target thread's interrupt status flag to true . If the target thread is currently blocked in Thread.sleep() , Object.wait() , or Thread.join() , the blocking call throws InterruptedException and clears the interrupt status flag back to false .
t.isInterrupted() : Instance method that queries the target thread's interrupt status, returning true or false . It does not alter the interrupt flag.
Thread.interrupted() : Static method that queries the current executing thread's interrupt status and clears the flag (resets it to false ). public class InterruptibleTask implements Runnable {
- @Override
- public void run() {
- while (!Thread.currentThread().isInterrupted()) {
- try {
- // Simulating work
- Thread.sleep(500);
- } catch (InterruptedException e) {
- System.out.println("Caught InterruptedException! Interrupt flag is now: "
+ Thread.currentThread().isInterrupted()); // Prints: false!
- // Best practice: restore interrupted status or exit
- Thread.currentThread().interrupt();
- break;
- }
- }
- }
- }
Note
Notice the static vs. instance method distinction: Thread.interrupted() always queries and clears the status of the thread invoking the method, regardless of which Thread reference it is called on. In contrast, t.isInterrupted() inspects thread t without clearing its state.
Thread Coordination with join() and sleep()
Thread.sleep(long millis) : Suspends the currently executing thread for at least the specified millisecond duration. The thread transitions to TIMED_WAITING . Crucially, Thread.sleep() does not release any held locks or monitors .
t.join() : Causes the current thread to pause and transition to WAITING until the target thread t terminates. An overloaded variant t.join(long millis) transitions the caller to TIMED_WAITING . Both variants throw InterruptedException if the calling thread is interrupted while waiting.