google-site-verification: google61fe8ba583a51912.html
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