The Thread class is the fundamental way to create and manage threads in Java. It provides methods to create, control, and synchronize threads.
1. Creating Threads by Extending Thread Class
Basic Thread Creation
public class BasicThreadCreation {
// Method 1: Extending Thread class
static class MyThread extends Thread {
private String threadName;
public MyThread(String name) {
this.threadName = name;
}
@Override
public void run() {
System.out.println(threadName + " is running...");
// Simulate some work
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " - Count: " + i);
try {
Thread.sleep(1000); // Pause for 1 second
} catch (InterruptedException e) {
System.out.println(threadName + " was interrupted!");
return;
}
}
System.out.println(threadName + " finished execution.");
}
}
public static void main(String[] args) {
System.out.println("=== Basic Thread Creation by Extending Thread Class ===");
System.out.println("Main thread started: " + Thread.currentThread().getName());
// Create thread instances
MyThread thread1 = new MyThread("Thread-1");
MyThread thread2 = new MyThread("Thread-2");
MyThread thread3 = new MyThread("Thread-3");
// Start the threads
thread1.start();
thread2.start();
thread3.start();
// Main thread continues execution
System.out.println("Main thread is doing other work...");
// Wait for all threads to complete
try {
thread1.join();
thread2.join();
thread3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All threads completed. Main thread finishing.");
}
}
Thread with Custom Properties
public class ThreadWithProperties {
static class WorkerThread extends Thread {
private int taskCount;
private long delay;
public WorkerThread(String name, int taskCount, long delay) {
super(name); // Set thread name
this.taskCount = taskCount;
this.delay = delay;
this.setPriority(Thread.NORM_PRIORITY); // Set thread priority
}
@Override
public void run() {
System.out.println(getName() + " started with priority: " + getPriority());
System.out.println(getName() + " is " + (isDaemon() ? "daemon" : "user") + " thread");
for (int i = 1; i <= taskCount; i++) {
System.out.println(getName() + " - Task " + i + "/" + taskCount);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
System.out.println(getName() + " was interrupted during task " + i);
return;
}
// Check if thread is interrupted
if (Thread.interrupted()) {
System.out.println(getName() + " received interrupt signal");
return;
}
}
System.out.println(getName() + " completed all tasks.");
}
}
public static void main(String[] args) {
System.out.println("=== Thread with Custom Properties ===");
// Create threads with different properties
WorkerThread normalThread = new WorkerThread("Normal-Worker", 5, 1000);
WorkerThread fastThread = new WorkerThread("Fast-Worker", 8, 500);
// Set thread priorities
normalThread.setPriority(Thread.MIN_PRIORITY); // Priority 1
fastThread.setPriority(Thread.MAX_PRIORITY); // Priority 10
// Create a daemon thread
WorkerThread daemonThread = new WorkerThread("Daemon-Worker", 10, 300);
daemonThread.setDaemon(true); // This thread will terminate when main thread ends
System.out.println("Main thread priority: " + Thread.currentThread().getPriority());
// Start all threads
normalThread.start();
fastThread.start();
daemonThread.start();
// Demonstrate thread states
demonstrateThreadStates(normalThread, fastThread, daemonThread);
// Wait for non-daemon threads to complete
try {
normalThread.join();
fastThread.join();
// Don't join daemon thread - it will terminate when main ends
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread completed.");
}
public static void demonstrateThreadStates(Thread... threads) {
System.out.println("\n=== Thread States ===");
for (Thread thread : threads) {
System.out.println(thread.getName() + " state: " + thread.getState());
}
// Check states after a short delay
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("\n=== Thread States After 100ms ===");
for (Thread thread : threads) {
System.out.println(thread.getName() + " state: " + thread.getState());
}
}
}
2. Thread Lifecycle and States
Understanding Thread States
public class ThreadLifecycle {
static class StateMonitoringThread extends Thread {
private Thread targetThread;
public StateMonitoringThread(Thread targetThread) {
super("State-Monitor");
this.targetThread = targetThread;
this.setDaemon(true);
}
@Override
public void run() {
while (targetThread.isAlive()) {
Thread.State state = targetThread.getState();
System.out.println("[" + new java.util.Date() + "] " +
targetThread.getName() + " state: " + state);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
break;
}
}
System.out.println(targetThread.getName() + " is no longer alive.");
}
}
static class WorkThread extends Thread {
private int workDuration;
public WorkThread(String name, int workDuration) {
super(name);
this.workDuration = workDuration;
}
@Override
public void run() {
System.out.println(getName() + " - Starting work for " + workDuration + "ms");
// NEW -> RUNNABLE
System.out.println(getName() + " - State after start: " + getState());
// Simulate work with TIMED_WAITING state
for (int i = 1; i <= 3; i++) {
System.out.println(getName() + " - Working phase " + i);
try {
// Thread goes to TIMED_WAITING state
Thread.sleep(workDuration / 3);
} catch (InterruptedException e) {
System.out.println(getName() + " - Interrupted during work");
return;
}
}
// Simulate BLOCKED state
synchronized (ThreadLifecycle.class) {
System.out.println(getName() + " - Entered synchronized block");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + " - Work completed");
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Thread Lifecycle and States ===");
WorkThread worker1 = new WorkThread("Worker-1", 1500);
WorkThread worker2 = new WorkThread("Worker-2", 1200);
// Start monitoring before starting workers
StateMonitoringThread monitor1 = new StateMonitoringThread(worker1);
StateMonitoringThread monitor2 = new StateMonitoringThread(worker2);
monitor1.start();
monitor2.start();
System.out.println("Worker-1 state before start: " + worker1.getState()); // NEW
// Start first worker
worker1.start();
Thread.sleep(100); // Small delay to see state change
System.out.println("Worker-1 state after start: " + worker1.getState()); // RUNNABLE/TIMED_WAITING
// Start second worker
worker2.start();
// Demonstrate WAITING state
demonstrateWaitingState();
// Wait for workers to complete
worker1.join();
worker2.join();
System.out.println("Worker-1 final state: " + worker1.getState()); // TERMINATED
System.out.println("Worker-2 final state: " + worker2.getState()); // TERMINATED
System.out.println("\nMain thread completed.");
}
public static void demonstrateWaitingState() throws InterruptedException {
System.out.println("\n=== Demonstrating WAITING State ===");
Object lock = new Object();
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
try {
System.out.println(Thread.currentThread().getName() + " - Going to wait state");
lock.wait(); // WAITING state
System.out.println(Thread.currentThread().getName() + " - Resumed from wait");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Waiting-Thread");
Thread notifyingThread = new Thread(() -> {
try {
Thread.sleep(1000); // Let waiting thread enter wait state
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " - Notifying waiting thread");
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Notifying-Thread");
waitingThread.start();
notifyingThread.start();
// Monitor the waiting thread state
Thread.sleep(500);
System.out.println("Waiting-Thread state after 500ms: " + waitingThread.getState()); // WAITING
waitingThread.join();
notifyingThread.join();
}
}
3. Thread Methods and Control
Thread Control Methods
public class ThreadControlMethods {
static class ControlledThread extends Thread {
private volatile boolean running = true;
private int counter = 0;
public ControlledThread(String name) {
super(name);
}
public void stopGracefully() {
running = false;
interrupt(); // Interrupt if sleeping/waiting
}
@Override
public void run() {
System.out.println(getName() + " started.");
while (running && !isInterrupted()) {
counter++;
System.out.println(getName() + " - Counter: " + counter);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(getName() + " - Sleep interrupted!");
// Restore interrupt status
Thread.currentThread().interrupt();
}
// Check if we should yield to other threads
if (counter % 3 == 0) {
System.out.println(getName() + " - Yielding to other threads");
Thread.yield();
}
}
System.out.println(getName() + " stopped. Final counter: " + counter);
}
public int getCounter() {
return counter;
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Thread Control Methods ===");
ControlledThread thread1 = new ControlledThread("Controlled-Thread-1");
ControlledThread thread2 = new ControlledThread("Controlled-Thread-2");
// Start threads
thread1.start();
thread2.start();
// Let threads run for some time
Thread.sleep(5000);
System.out.println("\n=== Stopping Threads Gracefully ===");
// Stop thread1 gracefully
System.out.println("Stopping " + thread1.getName());
thread1.stopGracefully();
// Let thread2 run a bit longer
Thread.sleep(2000);
System.out.println("Stopping " + thread2.getName());
thread2.stopGracefully();
// Wait for threads to finish
thread1.join();
thread2.join();
System.out.println("\nFinal counters:");
System.out.println(thread1.getName() + ": " + thread1.getCounter());
System.out.println(thread2.getName() + ": " + thread2.getCounter());
demonstrateThreadInterruption();
demonstrateThreadJoinWithTimeout();
}
public static void demonstrateThreadInterruption() throws InterruptedException {
System.out.println("\n=== Thread Interruption Demo ===");
Thread interruptibleThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " - Starting work");
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " - Working...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " - Interrupted during sleep!");
// Important: When InterruptedException is caught,
// interrupt status is cleared. We should re-interrupt.
Thread.currentThread().interrupt();
}
}
System.out.println(Thread.currentThread().getName() + " - Cleanup before exit");
}, "Interruptible-Thread");
interruptibleThread.start();
// Let thread run for 3 seconds
Thread.sleep(3000);
// Interrupt the thread
System.out.println("Main - Interrupting " + interruptibleThread.getName());
interruptibleThread.interrupt();
interruptibleThread.join();
System.out.println("Interruptible thread completed.");
}
public static void demonstrateThreadJoinWithTimeout() throws InterruptedException {
System.out.println("\n=== Thread Join with Timeout ===");
Thread longRunningThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " - Starting long task");
try {
Thread.sleep(5000); // Simulate long task
System.out.println(Thread.currentThread().getName() + " - Long task completed");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " - Interrupted!");
}
}, "Long-Running-Thread");
longRunningThread.start();
// Wait for thread with timeout
System.out.println("Main - Waiting for long-running thread with 2 second timeout");
longRunningThread.join(2000); // Wait max 2 seconds
if (longRunningThread.isAlive()) {
System.out.println("Main - Thread is still alive after timeout, interrupting...");
longRunningThread.interrupt();
} else {
System.out.println("Main - Thread completed within timeout");
}
longRunningThread.join(); // Wait for final completion
}
}
4. Thread Synchronization
Synchronized Methods and Blocks
public class ThreadSynchronization {
static class SharedResource {
private int counter = 0;
private final Object lock = new Object();
// Synchronized method
public synchronized void increment() {
counter++;
System.out.println(Thread.currentThread().getName() + " - Counter: " + counter);
try {
Thread.sleep(100); // Simulate some work
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Synchronized block with custom lock
public void decrement() {
synchronized (lock) {
if (counter > 0) {
counter--;
System.out.println(Thread.currentThread().getName() + " - Counter: " + counter);
}
}
}
public int getCounter() {
return counter;
}
}
static class CounterThread extends Thread {
private SharedResource resource;
private boolean increment;
public CounterThread(String name, SharedResource resource, boolean increment) {
super(name);
this.resource = resource;
this.increment = increment;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (increment) {
resource.increment();
} else {
resource.decrement();
}
try {
Thread.sleep(50); // Small delay between operations
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Thread Synchronization ===");
SharedResource resource = new SharedResource();
// Create multiple threads accessing shared resource
CounterThread incrementer1 = new CounterThread("Incrementer-1", resource, true);
CounterThread incrementer2 = new CounterThread("Incrementer-2", resource, true);
CounterThread decrementer1 = new CounterThread("Decrementer-1", resource, false);
incrementer1.start();
incrementer2.start();
decrementer1.start();
// Wait for all threads to complete
incrementer1.join();
incrementer2.join();
decrementer1.join();
System.out.println("Final counter value: " + resource.getCounter());
demonstrateProducerConsumer();
}
public static void demonstrateProducerConsumer() throws InterruptedException {
System.out.println("\n=== Producer-Consumer Problem ===");
class MessageQueue {
private String message;
private boolean hasMessage = false;
public synchronized void put(String message) {
while (hasMessage) {
try {
wait(); // Wait for consumer to consume
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.message = message;
hasMessage = true;
System.out.println("Produced: " + message);
notifyAll(); // Notify waiting consumers
}
public synchronized String take() {
while (!hasMessage) {
try {
wait(); // Wait for producer to produce
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String result = message;
hasMessage = false;
System.out.println("Consumed: " + result);
notifyAll(); // Notify waiting producers
return result;
}
}
MessageQueue queue = new MessageQueue();
// Producer thread
Thread producer = new Thread(() -> {
String[] messages = {"Message 1", "Message 2", "Message 3", "Message 4", "DONE"};
for (String message : messages) {
queue.put(message);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Producer");
// Consumer thread
Thread consumer = new Thread(() -> {
String message;
do {
message = queue.take();
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (!"DONE".equals(message));
}, "Consumer");
producer.start();
consumer.start();
producer.join();
consumer.join();
System.out.println("Producer-Consumer demo completed.");
}
}
5. Real-World Thread Examples
File Processing with Multiple Threads
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
public class FileProcessingThreads {
static class FileProcessor extends Thread {
private File file;
private CountDownLatch latch;
private Map<String, Integer> wordCount;
public FileProcessor(File file, CountDownLatch latch, Map<String, Integer> wordCount) {
super("FileProcessor-" + file.getName());
this.file = file;
this.latch = latch;
this.wordCount = wordCount;
}
@Override
public void run() {
System.out.println(getName() + " - Processing file: " + file.getName());
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
String[] words = line.split("\\s+");
for (String word : words) {
if (!word.trim().isEmpty()) {
synchronized (wordCount) {
wordCount.merge(word.toLowerCase(), 1, Integer::sum);
}
}
}
}
} catch (IOException e) {
System.err.println(getName() + " - Error processing file: " + e.getMessage());
} finally {
System.out.println(getName() + " - Completed processing: " + file.getName());
latch.countDown(); // Signal completion
}
}
}
static class ResultAggregator extends Thread {
private CountDownLatch latch;
private Map<String, Integer> wordCount;
public ResultAggregator(CountDownLatch latch, Map<String, Integer> wordCount) {
super("ResultAggregator");
this.latch = latch;
this.wordCount = wordCount;
}
@Override
public void run() {
try {
System.out.println("ResultAggregator - Waiting for all file processors...");
latch.await(); // Wait for all file processors to complete
System.out.println("\n=== Word Count Results ===");
// Display top 10 most frequent words
wordCount.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.limit(10)
.forEach(entry ->
System.out.printf("%-15s: %d%n", entry.getKey(), entry.getValue()));
System.out.println("Total unique words: " + wordCount.size());
} catch (InterruptedException e) {
System.err.println("ResultAggregator - Interrupted while waiting");
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== File Processing with Multiple Threads ===");
// Create sample text files for demonstration
createSampleFiles();
File directory = new File("sample_files");
File[] files = directory.listFiles((dir, name) -> name.endsWith(".txt"));
if (files == null || files.length == 0) {
System.out.println("No text files found in sample_files directory");
return;
}
Map<String, Integer> wordCount = new ConcurrentHashMap<>();
CountDownLatch latch = new CountDownLatch(files.length);
// Start file processor threads
List<FileProcessor> processors = new ArrayList<>();
for (File file : files) {
FileProcessor processor = new FileProcessor(file, latch, wordCount);
processors.add(processor);
processor.start();
}
// Start result aggregator
ResultAggregator aggregator = new ResultAggregator(latch, wordCount);
aggregator.start();
// Monitor progress
monitorProgress(processors, latch);
// Wait for all threads to complete
for (FileProcessor processor : processors) {
processor.join();
}
aggregator.join();
System.out.println("File processing completed.");
// Cleanup sample files
cleanupSampleFiles();
}
private static void createSampleFiles() {
File directory = new File("sample_files");
if (!directory.exists()) {
directory.mkdir();
}
String[] sampleTexts = {
"Java is a programming language and computing platform",
"Multithreading allows concurrent execution of two or more threads",
"Threads can be created by extending Thread class or implementing Runnable",
"Synchronization is important for thread safety in Java applications",
"File processing with multiple threads can improve performance significantly"
};
for (int i = 0; i < sampleTexts.length; i++) {
try (PrintWriter writer = new PrintWriter(new File(directory, "file" + (i + 1) + ".txt"))) {
// Write multiple lines to make files more substantial
for (int j = 0; j < 10; j++) {
writer.println(sampleTexts[i] + " - Line " + (j + 1));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
System.out.println("Created " + sampleTexts.length + " sample files");
}
private static void cleanupSampleFiles() {
File directory = new File("sample_files");
if (directory.exists()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
file.delete();
}
}
directory.delete();
System.out.println("Cleaned up sample files");
}
}
private static void monitorProgress(List<FileProcessor> processors, CountDownLatch latch) {
Thread monitorThread = new Thread(() -> {
while (latch.getCount() > 0) {
System.out.printf("Progress: %d/%d files completed%n",
processors.size() - latch.getCount(), processors.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
break;
}
}
}, "Progress-Monitor");
monitorThread.setDaemon(true);
monitorThread.start();
}
}
Web Page Downloader with Threads
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
public class WebPageDownloader {
static class PageDownloader extends Thread {
private String url;
private String savePath;
private CountDownLatch latch;
public PageDownloader(String url, String savePath, CountDownLatch latch) {
super("Downloader-" + new File(url).getName());
this.url = url;
this.savePath = savePath;
this.latch = latch;
}
@Override
public void run() {
System.out.println(getName() + " - Starting download: " + url);
try {
URL website = new URL(url);
try (InputStream in = website.openStream();
FileOutputStream out = new FileOutputStream(savePath)) {
byte[] buffer = new byte[1024];
int bytesRead;
long totalBytes = 0;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
// Check for interruption
if (Thread.currentThread().isInterrupted()) {
System.out.println(getName() + " - Download interrupted");
return;
}
}
System.out.println(getName() + " - Download completed: " +
totalBytes + " bytes saved to " + savePath);
} catch (IOException e) {
System.err.println(getName() + " - Error downloading: " + e.getMessage());
}
} catch (MalformedURLException e) {
System.err.println(getName() + " - Invalid URL: " + url);
} finally {
latch.countDown();
}
}
}
static class DownloadManager {
private List<PageDownloader> downloaders = new ArrayList<>();
private CountDownLatch latch;
public void addDownload(String url, String savePath) {
if (latch != null) {
throw new IllegalStateException("Downloads already started");
}
downloaders.add(new PageDownloader(url, savePath, latch));
}
public void startDownloads() throws InterruptedException {
if (downloaders.isEmpty()) {
System.out.println("No downloads added");
return;
}
latch = new CountDownLatch(downloaders.size());
// Update all downloaders with the latch
for (PageDownloader downloader : downloaders) {
// We need to recreate downloaders with the latch
// This is a limitation of our current design
}
System.out.println("Starting " + downloaders.size() + " downloads...");
// Start all downloaders
for (PageDownloader downloader : downloaders) {
downloader.start();
}
// Monitor progress
monitorProgress();
// Wait for all downloads to complete
latch.await();
System.out.println("All downloads completed!");
}
public void stopAllDownloads() {
System.out.println("Stopping all downloads...");
for (PageDownloader downloader : downloaders) {
if (downloader.isAlive()) {
downloader.interrupt();
}
}
}
private void monitorProgress() {
Thread monitor = new Thread(() -> {
while (latch.getCount() > 0) {
long completed = downloaders.size() - latch.getCount();
System.out.printf("Download Progress: %d/%d (%.1f%%)%n",
completed, downloaders.size(),
(completed * 100.0 / downloaders.size()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}, "Download-Monitor");
monitor.setDaemon(true);
monitor.start();
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Web Page Downloader with Threads ===");
DownloadManager manager = new DownloadManager();
// Add some sample URLs (using small files for demonstration)
manager.addDownload("https://www.example.com", "downloads/example.html");
manager.addDownload("https://httpbin.org/json", "downloads/json.json");
manager.addDownload("https://httpbin.org/xml", "downloads/xml.xml");
// Create downloads directory
new File("downloads").mkdir();
// Start downloads
try {
manager.startDownloads();
} catch (InterruptedException e) {
System.err.println("Download process interrupted");
manager.stopAllDownloads();
}
// Demonstrate timeout mechanism
demonstrateDownloadWithTimeout();
}
public static void demonstrateDownloadWithTimeout() throws InterruptedException {
System.out.println("\n=== Download with Timeout ===");
CountDownLatch latch = new CountDownLatch(1);
PageDownloader slowDownloader = new PageDownloader(
"https://httpbin.org/delay/5", // This URL delays response by 5 seconds
"downloads/slow.html",
latch
);
slowDownloader.start();
// Wait for download with timeout
boolean completed = latch.await(3, TimeUnit.SECONDS); // Wait max 3 seconds
if (!completed) {
System.out.println("Download timed out - interrupting...");
slowDownloader.interrupt();
slowDownloader.join(1000); // Wait for thread to finish
} else {
System.out.println("Download completed within timeout");
}
if (slowDownloader.isAlive()) {
System.out.println("Thread is still alive after interrupt, using stop (not recommended)");
// Note: Thread.stop() is deprecated and should be avoided
}
}
}
Summary
Key Thread Class Methods:
- Lifecycle Methods:
start()- Begins thread executionrun()- Contains thread's code (override this)join()- Waits for thread to dieinterrupt()- Interrupts the thread
- State Methods:
getState()- Returns thread stateisAlive()- Tests if thread is aliveisInterrupted()- Tests if thread is interrupted
- Control Methods:
sleep()- Causes thread to sleepyield()- Hint to scheduler to yield executionsetPriority()- Changes thread prioritysetDaemon()- Marks as daemon thread
Thread States:
- NEW - Created but not started
- RUNNABLE - Executing in JVM
- BLOCKED - Waiting for monitor lock
- WAITING - Waiting indefinitely
- TIMED_WAITING - Waiting with timeout
- TERMINATED - Completed execution
Best Practices:
- Always check interruption status in long-running tasks
- Use synchronization for shared resources
- Prefer implementing Runnable over extending Thread for flexibility
- Use thread pools for managing multiple threads
- Handle InterruptedException properly
- Avoid Thread.stop() - use interruption instead
- Use daemon threads for background tasks that shouldn't prevent JVM exit
The Thread class provides the foundation for multithreading in Java, but for most applications, using the Executor framework (thread pools) is recommended for better resource management and control.