Implementing Runnable Interface in Java

The Runnable interface is the preferred way to create threads in Java as it provides more flexibility and follows better object-oriented design principles.

1. Basic Runnable Implementation

Creating Threads with Runnable

public class BasicRunnableImplementation {
// Method 1: Implementing Runnable with a separate class
static class MyRunnable implements Runnable {
private String threadName;
public MyRunnable(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 Runnable Implementation ===");
System.out.println("Main thread started: " + Thread.currentThread().getName());
// Create Runnable instances
Runnable task1 = new MyRunnable("Runnable-1");
Runnable task2 = new MyRunnable("Runnable-2");
Runnable task3 = new MyRunnable("Runnable-3");
// Create Thread objects with Runnables
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
Thread thread3 = new Thread(task3);
// 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.");
// Demonstrate different ways to create Runnables
demonstrateVariousRunnableCreations();
}
public static void demonstrateVariousRunnableCreations() {
System.out.println("\n=== Various Ways to Create Runnables ===");
// Method 2: Anonymous inner class
Runnable anonymousRunnable = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous Runnable is running");
for (int i = 0; i < 3; i++) {
System.out.println("Anonymous - Working: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// Method 3: Lambda expression (Java 8+)
Runnable lambdaRunnable = () -> {
System.out.println("Lambda Runnable is running");
for (int i = 0; i < 3; i++) {
System.out.println("Lambda - Working: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// Method 4: Method reference
Runnable methodRefRunnable = BasicRunnableImplementation::runTask;
// Create and start threads
Thread t1 = new Thread(anonymousRunnable, "Anonymous-Thread");
Thread t2 = new Thread(lambdaRunnable, "Lambda-Thread");
Thread t3 = new Thread(methodRefRunnable, "MethodRef-Thread");
t1.start();
t2.start();
t3.start();
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void runTask() {
System.out.println("Method Reference Runnable is running");
for (int i = 0; i < 3; i++) {
System.out.println("MethodRef - Working: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Runnable with Parameters and Return Values

import java.util.concurrent.*;
public class RunnableWithParameters {
// Runnable that processes data and stores result
static class DataProcessor implements Runnable {
private final String data;
private final BlockingQueue<String> resultQueue;
public DataProcessor(String data, BlockingQueue<String> resultQueue) {
this.data = data;
this.resultQueue = resultQueue;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " processing: " + data);
try {
// Simulate processing time
Thread.sleep(2000);
// Process data (convert to uppercase)
String result = data.toUpperCase();
// Put result in queue
resultQueue.put(result);
System.out.println(Thread.currentThread().getName() + " completed: " + result);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " was interrupted");
Thread.currentThread().interrupt();
}
}
}
// Runnable with callback mechanism
static class TaskWithCallback implements Runnable {
private final int number;
private final Callback callback;
public interface Callback {
void onComplete(int input, int result);
void onError(int input, String error);
}
public TaskWithCallback(int number, Callback callback) {
this.number = number;
this.callback = callback;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " calculating square of " + number);
try {
// Simulate calculation
Thread.sleep(1000);
if (number < 0) {
callback.onError(number, "Negative numbers not allowed");
return;
}
int result = number * number;
callback.onComplete(number, result);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " was interrupted");
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Runnable with Parameters and Callbacks ===");
// Example 1: Using BlockingQueue for results
demonstrateBlockingQueueResults();
// Example 2: Using callback interface
demonstrateCallbackMechanism();
// Example 3: Using CompletableFuture
demonstrateCompletableFuture();
}
public static void demonstrateBlockingQueueResults() throws InterruptedException {
System.out.println("\n=== Using BlockingQueue for Results ===");
BlockingQueue<String> resultQueue = new LinkedBlockingQueue<>();
String[] dataItems = {"apple", "banana", "cherry", "date", "elderberry"};
// Create and start processor threads
Thread[] threads = new Thread[dataItems.length];
for (int i = 0; i < dataItems.length; i++) {
Runnable processor = new DataProcessor(dataItems[i], resultQueue);
threads[i] = new Thread(processor, "Processor-" + (i + 1));
threads[i].start();
}
// Collect results
System.out.println("Collecting results...");
for (int i = 0; i < dataItems.length; i++) {
String result = resultQueue.take();
System.out.println("Received result: " + result);
}
// Wait for all threads to complete
for (Thread thread : threads) {
thread.join();
}
System.out.println("All data processing completed.");
}
public static void demonstrateCallbackMechanism() {
System.out.println("\n=== Using Callback Mechanism ===");
TaskWithCallback.Callback callback = new TaskWithCallback.Callback() {
@Override
public void onComplete(int input, int result) {
System.out.println("Callback: Square of " + input + " is " + result);
}
@Override
public void onError(int input, String error) {
System.out.println("Callback: Error for " + input + " - " + error);
}
};
// Test with valid and invalid inputs
Thread validThread = new Thread(new TaskWithCallback(5, callback), "Valid-Thread");
Thread invalidThread = new Thread(new TaskWithCallback(-3, callback), "Invalid-Thread");
validThread.start();
invalidThread.start();
try {
validThread.join();
invalidThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void demonstrateCompletableFuture() {
System.out.println("\n=== Using CompletableFuture ===");
// Create Runnable that completes a CompletableFuture
Runnable futureTask = () -> {
System.out.println(Thread.currentThread().getName() + " executing future task");
try {
Thread.sleep(2000);
// Task completes successfully
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Task interrupted", e);
}
};
CompletableFuture<Void> future = CompletableFuture.runAsync(futureTask);
// Add completion handler
future.thenRun(() -> System.out.println("Future task completed successfully!"))
.exceptionally(throwable -> {
System.out.println("Future task failed: " + throwable.getMessage());
return null;
});
System.out.println("Main thread continues while future task runs...");
// Wait for completion
future.join();
System.out.println("Main thread completed after future.");
}
}

2. Runnable vs Thread Class

Comparison and Best Practices

public class RunnableVsThread {
// Approach 1: Extending Thread class
static class ThreadExtension extends Thread {
private String taskName;
public ThreadExtension(String name) {
super(name);
this.taskName = name;
}
@Override
public void run() {
System.out.println(taskName + " (Thread extension) is running");
performTask();
}
private void performTask() {
for (int i = 1; i <= 3; i++) {
System.out.println(taskName + " - Step " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println(taskName + " interrupted");
return;
}
}
}
}
// Approach 2: Implementing Runnable interface
static class RunnableImplementation implements Runnable {
private String taskName;
public RunnableImplementation(String name) {
this.taskName = name;
}
@Override
public void run() {
System.out.println(taskName + " (Runnable) is running");
performTask();
}
private void performTask() {
for (int i = 1; i <= 3; i++) {
System.out.println(taskName + " - Step " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println(taskName + " interrupted");
return;
}
}
}
}
public static void main(String[] args) {
System.out.println("=== Runnable vs Thread Extension Comparison ===");
// Test Thread extension approach
System.out.println("\n--- Thread Extension Approach ---");
ThreadExtension thread1 = new ThreadExtension("Thread-Ext-1");
ThreadExtension thread2 = new ThreadExtension("Thread-Ext-2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Test Runnable implementation approach
System.out.println("\n--- Runnable Implementation Approach ---");
Runnable runnable1 = new RunnableImplementation("Runnable-1");
Runnable runnable2 = new RunnableImplementation("Runnable-2");
Thread thread3 = new Thread(runnable1);
Thread thread4 = new Thread(runnable2);
Thread thread5 = new Thread(runnable1); // Can reuse same Runnable
thread3.start();
thread4.start();
// thread5.start(); // Would share state with thread3
try {
thread3.join();
thread4.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
demonstrateAdvantagesOfRunnable();
}
public static void demonstrateAdvantagesOfRunnable() {
System.out.println("\n=== Advantages of Runnable Interface ===");
// 1. Better OOP - Can extend another class
class BusinessLogic {
public void process() {
System.out.println("Processing business logic...");
}
}
class SmartRunnable extends BusinessLogic implements Runnable {
@Override
public void run() {
process(); // Can use parent class methods
System.out.println("Also running as thread!");
}
}
// 2. Thread pool compatibility
Runnable task = () -> {
System.out.println(Thread.currentThread().getName() + " executing task");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// Can use same task with multiple threads
for (int i = 0; i < 3; i++) {
new Thread(task, "Worker-" + i).start();
}
// 3. Lambda support
Runnable lambdaTask = () -> System.out.println("Lambda task running");
new Thread(lambdaTask).start();
// 4. Functional programming style
executeWithRetry(() -> {
System.out.println("Retryable task executing");
if (Math.random() > 0.5) {
throw new RuntimeException("Simulated failure");
}
System.out.println("Task succeeded!");
}, 3);
}
public static void executeWithRetry(Runnable task, int maxRetries) {
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
System.out.println("Attempt " + attempt + " of " + maxRetries);
task.run();
System.out.println("Task completed successfully on attempt " + attempt);
return;
} catch (Exception e) {
System.out.println("Attempt " + attempt + " failed: " + e.getMessage());
if (attempt == maxRetries) {
System.out.println("All attempts failed");
}
}
}
}
}

3. Runnable in Executor Framework

Using Runnable with Executors

import java.util.concurrent.*;
import java.util.*;
public class RunnableWithExecutors {
static class MonitoringTask implements Runnable {
private final String taskName;
private final int duration;
public MonitoringTask(String name, int duration) {
this.taskName = name;
this.duration = duration;
}
@Override
public void run() {
System.out.println("[" + new Date() + "] " + taskName + " started on " + 
Thread.currentThread().getName());
try {
// Simulate work
for (int i = 1; i <= duration; i++) {
System.out.println("[" + new Date() + "] " + taskName + 
" - Progress: " + i + "/" + duration + 
" on " + Thread.currentThread().getName());
Thread.sleep(1000);
// Check for interruption
if (Thread.currentThread().isInterrupted()) {
System.out.println(taskName + " was interrupted!");
return;
}
}
System.out.println("[" + new Date() + "] " + taskName + " completed successfully");
} catch (InterruptedException e) {
System.out.println(taskName + " was interrupted during sleep");
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Runnable with Executor Framework ===");
// Example 1: SingleThreadExecutor
demonstrateSingleThreadExecutor();
// Example 2: FixedThreadPool
demonstrateFixedThreadPool();
// Example 3: CachedThreadPool
demonstrateCachedThreadPool();
// Example 4: ScheduledExecutorService
demonstrateScheduledExecutor();
// Example 5: Using Future with Runnable
demonstrateFutureWithRunnable();
}
public static void demonstrateSingleThreadExecutor() throws InterruptedException {
System.out.println("\n=== SingleThreadExecutor ===");
ExecutorService executor = Executors.newSingleThreadExecutor();
// Submit multiple tasks - they will execute sequentially
for (int i = 1; i <= 3; i++) {
Runnable task = new MonitoringTask("SingleThread-Task-" + i, 2);
executor.submit(task);
}
// Shutdown the executor
executor.shutdown();
// Wait for termination
boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("Executor terminated: " + terminated);
}
public static void demonstrateFixedThreadPool() throws InterruptedException {
System.out.println("\n=== FixedThreadPool (3 threads) ===");
ExecutorService executor = Executors.newFixedThreadPool(3);
// Submit more tasks than threads
for (int i = 1; i <= 6; i++) {
Runnable task = new MonitoringTask("FixedPool-Task-" + i, 3);
executor.submit(task);
}
executor.shutdown();
executor.awaitTermination(15, TimeUnit.SECONDS);
}
public static void demonstrateCachedThreadPool() throws InterruptedException {
System.out.println("\n=== CachedThreadPool ===");
ExecutorService executor = Executors.newCachedThreadPool();
// Submit many short-lived tasks
List<Future<?>> futures = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
Runnable task = new MonitoringTask("CachedPool-Task-" + i, 1);
Future<?> future = executor.submit(task);
futures.add(future);
}
// Wait for all tasks to complete
for (Future<?> future : futures) {
try {
future.get(); // Wait for completion
} catch (ExecutionException e) {
System.out.println("Task execution failed: " + e.getCause().getMessage());
}
}
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
public static void demonstrateScheduledExecutor() throws InterruptedException {
System.out.println("\n=== ScheduledExecutorService ===");
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// Schedule a task to run after initial delay
System.out.println("Scheduling one-time task to run in 2 seconds...");
ScheduledFuture<?> oneTimeFuture = scheduler.schedule(
() -> System.out.println("One-time task executed!"), 
2, TimeUnit.SECONDS
);
// Schedule a task to run repeatedly
System.out.println("Scheduling periodic task to run every 3 seconds...");
ScheduledFuture<?> periodicFuture = scheduler.scheduleAtFixedRate(
() -> System.out.println("Periodic task executed at " + new Date()),
1, 3, TimeUnit.SECONDS
);
// Let it run for some time
Thread.sleep(10000);
// Cancel periodic task
System.out.println("Cancelling periodic task...");
periodicFuture.cancel(true);
scheduler.shutdown();
scheduler.awaitTermination(5, TimeUnit.SECONDS);
}
public static void demonstrateFutureWithRunnable() throws InterruptedException {
System.out.println("\n=== Using Future with Runnable ===");
ExecutorService executor = Executors.newFixedThreadPool(2);
// Runnable that doesn't return a value but we can track completion
Runnable task = () -> {
System.out.println("Task with Future is running...");
try {
Thread.sleep(3000);
System.out.println("Task completed");
} catch (InterruptedException e) {
System.out.println("Task interrupted");
Thread.currentThread().interrupt();
}
};
// Submit task and get Future
Future<?> future = executor.submit(task);
System.out.println("Task submitted, checking status...");
// Check if task is done
while (!future.isDone()) {
System.out.println("Task still running...");
Thread.sleep(1000);
}
System.out.println("Task is done: " + future.isDone());
// Try to get result (always null for Runnable)
try {
Object result = future.get();
System.out.println("Task result: " + result); // Will be null
} catch (ExecutionException e) {
System.out.println("Task execution failed: " + e.getCause().getMessage());
}
executor.shutdown();
}
}

4. Advanced Runnable Patterns

Runnable with Resource Management

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class AdvancedRunnablePatterns {
// Runnable with resource cleanup
static class ResourceTask implements Runnable {
private final String taskName;
private final AtomicBoolean shutdownRequested;
public ResourceTask(String name, AtomicBoolean shutdown) {
this.taskName = name;
this.shutdownRequested = shutdown;
}
@Override
public void run() {
System.out.println(taskName + " - Initializing resources...");
// Simulate resource acquisition
try {
// Resource initialization
Thread.sleep(1000);
while (!shutdownRequested.get() && !Thread.currentThread().isInterrupted()) {
System.out.println(taskName + " - Processing data...");
Thread.sleep(2000);
// Simulate work
if (Math.random() < 0.1) {
throw new RuntimeException("Simulated processing error");
}
}
} catch (InterruptedException e) {
System.out.println(taskName + " - Interrupted during processing");
Thread.currentThread().interrupt();
} catch (Exception e) {
System.out.println(taskName + " - Error during processing: " + e.getMessage());
} finally {
// Always clean up resources
cleanup();
}
}
private void cleanup() {
System.out.println(taskName + " - Cleaning up resources...");
try {
Thread.sleep(500); // Simulate cleanup time
} catch (InterruptedException e) {
System.out.println(taskName + " - Interrupted during cleanup");
Thread.currentThread().interrupt();
}
System.out.println(taskName + " - Resources cleaned up");
}
}
// Runnable with phase execution
static class PhasedTask implements Runnable {
private final String taskName;
public PhasedTask(String name) {
this.taskName = name;
}
@Override
public void run() {
executePhase("Initialization", 1000);
if (Thread.currentThread().isInterrupted()) return;
executePhase("Data Loading", 1500);
if (Thread.currentThread().isInterrupted()) return;
executePhase("Processing", 2000);
if (Thread.currentThread().isInterrupted()) return;
executePhase("Cleanup", 500);
System.out.println(taskName + " - All phases completed successfully");
}
private void executePhase(String phaseName, long duration) {
System.out.println(taskName + " - Starting phase: " + phaseName);
try {
Thread.sleep(duration);
System.out.println(taskName + " - Completed phase: " + phaseName);
} catch (InterruptedException e) {
System.out.println(taskName + " - Interrupted during phase: " + phaseName);
Thread.currentThread().interrupt();
throw new RuntimeException("Phase " + phaseName + " interrupted", e);
}
}
}
// Runnable with retry logic
static class RetryableTask implements Runnable {
private final String taskName;
private final int maxRetries;
public RetryableTask(String name, int maxRetries) {
this.taskName = name;
this.maxRetries = maxRetries;
}
@Override
public void run() {
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
System.out.println(taskName + " - Attempt " + attempt + "/" + maxRetries);
executeWithPossibleFailure();
System.out.println(taskName + " - Succeeded on attempt " + attempt);
return;
} catch (RuntimeException e) {
System.out.println(taskName + " - Attempt " + attempt + " failed: " + e.getMessage());
if (attempt == maxRetries) {
System.out.println(taskName + " - All attempts failed");
throw e;
}
// Exponential backoff
long backoffTime = (long) (Math.pow(2, attempt - 1) * 1000);
System.out.println(taskName + " - Backing off for " + backoffTime + "ms");
try {
Thread.sleep(backoffTime);
} catch (InterruptedException ie) {
System.out.println(taskName + " - Interrupted during backoff");
Thread.currentThread().interrupt();
return;
}
}
}
}
private void executeWithPossibleFailure() {
// Simulate unreliable operation (30% failure rate)
if (Math.random() < 0.3) {
throw new RuntimeException("Simulated operation failure");
}
// Simulate work
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Operation interrupted", e);
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Advanced Runnable Patterns ===");
// Pattern 1: Resource management with shutdown signal
demonstrateResourceManagement();
// Pattern 2: Phased execution with interruption points
demonstratePhasedExecution();
// Pattern 3: Retry logic with exponential backoff
demonstrateRetryLogic();
// Pattern 4: Task composition
demonstrateTaskComposition();
}
public static void demonstrateResourceManagement() throws InterruptedException {
System.out.println("\n=== Resource Management Pattern ===");
AtomicBoolean shutdown = new AtomicBoolean(false);
ExecutorService executor = Executors.newFixedThreadPool(2);
// Start resource tasks
for (int i = 1; i <= 2; i++) {
Runnable task = new ResourceTask("ResourceTask-" + i, shutdown);
executor.submit(task);
}
// Let tasks run for some time
Thread.sleep(5000);
// Request shutdown
System.out.println("Requesting shutdown...");
shutdown.set(true);
executor.shutdown();
boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("Executor terminated gracefully: " + terminated);
}
public static void demonstratePhasedExecution() throws InterruptedException {
System.out.println("\n=== Phased Execution Pattern ===");
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(new PhasedTask("PhasedTask"));
// Let it run for a while then interrupt
Thread.sleep(2000);
System.out.println("Interrupting phased task...");
future.cancel(true); // Interrupt if running
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
public static void demonstrateRetryLogic() {
System.out.println("\n=== Retry Logic Pattern ===");
Thread retryThread = new Thread(new RetryableTask("RetryTask", 3));
retryThread.start();
try {
retryThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void demonstrateTaskComposition() {
System.out.println("\n=== Task Composition Pattern ===");
// Create composite task from multiple Runnables
Runnable compositeTask = () -> {
System.out.println("Starting composite task execution");
// Execute series of tasks
executeWithTiming("Task A", () -> {
try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); }
});
executeWithTiming("Task B", () -> {
try { Thread.sleep(1500); } catch (InterruptedException e) { throw new RuntimeException(e); }
});
executeWithTiming("Task C", () -> {
try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); }
});
System.out.println("Composite task completed");
};
new Thread(compositeTask, "Composite-Thread").start();
}
public static void executeWithTiming(String taskName, Runnable task) {
long startTime = System.currentTimeMillis();
System.out.println("Starting " + taskName);
task.run();
long duration = System.currentTimeMillis() - startTime;
System.out.println(taskName + " completed in " + duration + "ms");
}
}

5. Real-World Runnable Examples

Web Server Request Handler

import java.io.*;
import java.net.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class RealWorldRunnableExamples {
// Simulated web server request handler
static class RequestHandler implements Runnable {
private final Socket clientSocket;
private final int requestId;
private static final AtomicInteger requestCounter = new AtomicInteger(0);
public RequestHandler(Socket socket) {
this.clientSocket = socket;
this.requestId = requestCounter.incrementAndGet();
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("[" + new java.util.Date() + "] " + 
threadName + " handling request #" + requestId);
try (BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
// Read request
String requestLine = in.readLine();
System.out.println("Request #" + requestId + ": " + requestLine);
// Simulate processing time based on request type
if (requestLine != null && requestLine.contains("heavy")) {
processHeavyRequest();
} else {
processLightRequest();
}
// Send response
String response = "HTTP/1.1 200 OK\r\n\r\n" +
"<html><body><h1>Request #" + requestId + " processed</h1>" +
"<p>Handled by: " + threadName + "</p></body></html>";
out.println(response);
System.out.println("[" + new java.util.Date() + "] " + 
threadName + " completed request #" + requestId);
} catch (IOException e) {
System.err.println("Error handling request #" + requestId + ": " + e.getMessage());
} finally {
try {
clientSocket.close();
} catch (IOException e) {
System.err.println("Error closing socket for request #" + requestId);
}
}
}
private void processHeavyRequest() throws InterruptedException {
System.out.println("Processing heavy request #" + requestId);
// Simulate heavy processing (database queries, file I/O, etc.)
for (int i = 1; i <= 5; i++) {
System.out.println("Heavy request #" + requestId + " - Phase " + i);
Thread.sleep(1000);
}
}
private void processLightRequest() throws InterruptedException {
System.out.println("Processing light request #" + requestId);
// Simulate light processing
Thread.sleep(500);
}
}
// Background data processor
static class DataProcessor implements Runnable {
private final BlockingQueue<String> dataQueue;
private final AtomicBoolean shutdown;
private final String processorName;
public DataProcessor(String name, BlockingQueue<String> queue, AtomicBoolean shutdown) {
this.processorName = name;
this.dataQueue = queue;
this.shutdown = shutdown;
}
@Override
public void run() {
System.out.println(processorName + " started");
while (!shutdown.get() || !dataQueue.isEmpty()) {
try {
// Wait for data with timeout to allow shutdown check
String data = dataQueue.poll(1, TimeUnit.SECONDS);
if (data != null) {
processData(data);
}
} catch (InterruptedException e) {
System.out.println(processorName + " interrupted");
Thread.currentThread().interrupt();
break;
}
}
System.out.println(processorName + " shutdown complete");
}
private void processData(String data) {
System.out.println(processorName + " processing: " + data);
// Simulate data processing
try {
Thread.sleep(1000);
// Simulate occasional processing errors
if (Math.random() < 0.1) {
throw new RuntimeException("Simulated processing error for: " + data);
}
System.out.println(processorName + " completed: " + data);
} catch (InterruptedException e) {
System.out.println(processorName + " interrupted while processing: " + data);
Thread.currentThread().interrupt();
} catch (Exception e) {
System.err.println(processorName + " error processing " + data + ": " + e.getMessage());
// In real scenario, you might want to retry or move to dead letter queue
}
}
}
// Periodic health checker
static class HealthChecker implements Runnable {
private final String serviceName;
private final AtomicBoolean shutdown;
public HealthChecker(String serviceName, AtomicBoolean shutdown) {
this.serviceName = serviceName;
this.shutdown = shutdown;
}
@Override
public void run() {
System.out.println("Health checker for " + serviceName + " started");
int checkCount = 0;
while (!shutdown.get()) {
try {
checkCount++;
performHealthCheck(checkCount);
// Wait between checks
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("Health checker interrupted");
Thread.currentThread().interrupt();
break;
}
}
System.out.println("Health checker for " + serviceName + " stopped");
}
private void performHealthCheck(int checkNumber) {
boolean healthy = Math.random() > 0.2; // 80% healthy
if (healthy) {
System.out.println("[" + new java.util.Date() + "] " + 
serviceName + " health check #" + checkNumber + ": OK");
} else {
System.err.println("[" + new java.util.Date() + "] " + 
serviceName + " health check #" + checkNumber + ": UNHEALTHY");
// In real scenario, trigger alerts or recovery actions
}
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== Real-World Runnable Examples ===");
// Example 1: Simulated web server
demonstrateWebServer();
// Example 2: Data processing pipeline
demonstrateDataProcessingPipeline();
// Example 3: Background services
demonstrateBackgroundServices();
}
public static void demonstrateWebServer() throws Exception {
System.out.println("\n=== Simulated Web Server ===");
ExecutorService threadPool = Executors.newFixedThreadPool(3);
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("Web server listening on port 8080...");
// Shutdown hook for graceful shutdown
AtomicBoolean serverRunning = new AtomicBoolean(true);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Shutdown requested, stopping server...");
serverRunning.set(false);
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
threadPool.shutdown();
}));
// Handle requests for 10 seconds then shutdown
Thread serverStopper = new Thread(() -> {
try {
Thread.sleep(10000);
serverRunning.set(false);
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
});
serverStopper.start();
// Accept and handle connections
while (serverRunning.get()) {
try {
Socket clientSocket = serverSocket.accept();
Runnable handler = new RequestHandler(clientSocket);
threadPool.submit(handler);
} catch (IOException e) {
if (serverRunning.get()) {
System.err.println("Error accepting connection: " + e.getMessage());
}
}
}
// Shutdown gracefully
threadPool.shutdown();
boolean terminated = threadPool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("Web server shutdown: " + (terminated ? "graceful" : "forced"));
}
public static void demonstrateDataProcessingPipeline() throws InterruptedException {
System.out.println("\n=== Data Processing Pipeline ===");
BlockingQueue<String> dataQueue = new LinkedBlockingQueue<>();
AtomicBoolean shutdown = new AtomicBoolean(false);
// Create multiple data processors
ExecutorService processorPool = Executors.newFixedThreadPool(2);
for (int i = 1; i <= 2; i++) {
Runnable processor = new DataProcessor("Processor-" + i, dataQueue, shutdown);
processorPool.submit(processor);
}
// Data producer
Thread producer = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
String data = "DataItem-" + i;
try {
dataQueue.put(data);
System.out.println("Produced: " + data);
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("Producer interrupted");
break;
}
}
System.out.println("Producer finished");
});
producer.start();
// Wait for producer to finish
producer.join();
// Signal shutdown and wait for processors
shutdown.set(true);
processorPool.shutdown();
processorPool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("Data processing pipeline completed");
}
public static void demonstrateBackgroundServices() throws InterruptedException {
System.out.println("\n=== Background Services ===");
AtomicBoolean shutdown = new AtomicBoolean(false);
ExecutorService servicePool = Executors.newCachedThreadPool();
// Start multiple health checkers
String[] services = {"Database", "Cache", "API-Gateway", "File-System"};
for (String service : services) {
Runnable healthChecker = new HealthChecker(service, shutdown);
servicePool.submit(healthChecker);
}
// Let services run for 10 seconds
Thread.sleep(10000);
// Shutdown services
System.out.println("Shutting down background services...");
shutdown.set(true);
servicePool.shutdown();
servicePool.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("All background services stopped");
}
}

Summary

Key Advantages of Runnable over Thread:

  1. Better OOP Design: Can extend other classes
  2. Flexibility: Same Runnable can be used with different Thread instances
  3. Thread Pool Compatibility: Works seamlessly with Executor framework
  4. Lambda Support: Can be implemented with lambda expressions
  5. Separation of Concerns: Separates task definition from execution mechanism

Common Runnable Patterns:

  1. Task with Parameters: Use constructor to pass data
  2. Result Handling: Use callbacks, Futures, or shared data structures
  3. Resource Management: Implement proper cleanup in finally blocks
  4. Error Handling: Include proper exception handling and logging
  5. Shutdown Coordination: Use AtomicBoolean or other signaling mechanisms

Best Practices:

  1. Always handle InterruptedException properly
  2. Use thread pools (ExecutorService) for managing multiple tasks
  3. Implement proper resource cleanup in finally blocks
  4. Use volatile or atomic variables for shared state
  5. Consider using Callable when you need return values
  6. Use appropriate synchronization for shared resources

The Runnable interface provides a clean, flexible way to define tasks that can be executed concurrently, making it the preferred approach for most multithreading scenarios in Java.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper