Syntax
thread.join();\nThread.sleep(milliseconds);Examples
Thread.sleep()
Pausing execution for a specified duration.
public class SleepExample {
public static void main(String[] args) throws InterruptedException {
System.out.println("Starting...");
Thread.sleep(1000); // pause for 1000ms (1 second)
System.out.println("One second later");
}
}join(): Waiting for a Thread to Finish
Making the main thread wait for a worker thread to complete before continuing.
Thread worker = new Thread(() -> {
for (int i = 1; i <= 3; i++) {
System.out.println("Working: " + i);
}
});
worker.start();
try {
worker.join(); // main thread waits here until worker finishes
} catch (InterruptedException e) {
System.out.println("Interrupted while waiting");
}
System.out.println("Worker finished, continuing main thread");interrupt(): Requesting a Thread Stop
A cooperative way to signal a thread to stop, which the thread must check for itself.
Thread task = new Thread(() -> {
int count = 0;
while (!Thread.currentThread().isInterrupted()) {
count++;
if (count % 100_000_000 == 0) {
System.out.println("Still working...");
}
}
System.out.println("Task stopped, saw the interrupt signal");
});
task.start();
try {
Thread.sleep(50); // let it run briefly
task.interrupt(); // request it to stop
} catch (InterruptedException e) {
e.printStackTrace();
}Best practices
- Always handle InterruptedException from sleep() and join() - either propagate it, or re-set the interrupt flag with Thread.currentThread().interrupt() if you catch and swallow it
- Use join() when your program logic genuinely depends on a thread completing before proceeding, rather than guessing with an arbitrary sleep() delay
- Check Thread.currentThread().isInterrupted() periodically inside long-running tasks so they can respond promptly to interrupt() requests
- Remember interrupt() does not forcibly stop a thread - it only sets a flag that well-behaved code is expected to check and respect
At a glance
- Purpose
- General-purpose application development
- File extension
- .java
- Runs in
- Java Virtual Machine
- Usually used with
- JDK and Java libraries
Specifications & further reading
Related Java documentation
Creating Threads (Thread & Runnable)
A thread is an independent path of execution within a program, letting multiple tasks run seemingly simultaneously. Java offers two main ways to create a thread: extending the Thread class directly and overriding its run() method, or implementing the Runnable interface and passing it to a Thread - the Runnable approach is generally preferred, since Java doesn't support multiple inheritance and Runnable keeps your class free to extend something else.synchronized Keyword
The synchronized keyword prevents multiple threads from executing a block of code (or a method) at the same time on the same object, protecting shared, mutable data from race conditions. When one thread enters a synchronized block, it acquires a lock on the specified object; any other thread trying to enter a synchronized block on that same object must wait until the lock is released.ExecutorService & Thread Pools
ExecutorService, from java.util.concurrent, manages a pool of reusable threads, letting you submit tasks without manually creating and managing individual Thread objects. This avoids the overhead of constantly creating new threads and gives you control over how many run concurrently. Executors provides convenient factory methods for common pool configurations, like a fixed-size pool or a single-threaded executor.Atomic Variables
Classes like AtomicInteger, AtomicLong, and AtomicBoolean, from java.util.concurrent.atomic, provide thread-safe operations on single variables without needing explicit synchronized blocks. They use low-level, lock-free CPU instructions (compare-and-swap) to guarantee that operations like incrementAndGet() are atomic - performed as a single, uninterruptible step - even when many threads use them concurrently.
A thread is an independent path of execution within a program, letting multiple tasks run seemingly simultaneously. Java offers two main ways to create a thread: extending the Thread class directly and overriding its run() method, or implementing the Runnable interface and passing it to a Thread - the Runnable approach is generally preferred, since Java doesn't support multiple inheritance and Runnable keeps your class free to extend something else.synchronized Keyword
The synchronized keyword prevents multiple threads from executing a block of code (or a method) at the same time on the same object, protecting shared, mutable data from race conditions. When one thread enters a synchronized block, it acquires a lock on the specified object; any other thread trying to enter a synchronized block on that same object must wait until the lock is released.ExecutorService & Thread Pools
ExecutorService, from java.util.concurrent, manages a pool of reusable threads, letting you submit tasks without manually creating and managing individual Thread objects. This avoids the overhead of constantly creating new threads and gives you control over how many run concurrently. Executors provides convenient factory methods for common pool configurations, like a fixed-size pool or a single-threaded executor.Atomic Variables
Classes like AtomicInteger, AtomicLong, and AtomicBoolean, from java.util.concurrent.atomic, provide thread-safe operations on single variables without needing explicit synchronized blocks. They use low-level, lock-free CPU instructions (compare-and-swap) to guarantee that operations like incrementAndGet() are atomic - performed as a single, uninterruptible step - even when many threads use them concurrently.