September 3, 2022 How to Create and Start a New Thread in Java Table Of Contents Creating a New Thread By Extending Thread Class By Implementing Runnable Interface Using Lambda Expressions Starting a New Thread Using Thread.start() Using ExecutorService Delayed Execution with ScheduledExecutorService Using CompletableFuture 1. Creating a New Thread In Java, we can create a Thread in the following ways: By extending Thread class By implementing Runnable interface Using Lambda expressions 1.1. By Extending Thread Class To create a new thread, extend the class with Thread and override the run() method. class SubTask extends Thread { public void run() { System.out.println("SubTask started..."); } } 1.2. By Implementing Runnable Interface Implementing the Runnable interface is considered a better approach because, in this way, the thread class can extend any other class. Remember, in Java, a class can extend only one class but implement multiple interfaces. class SubTaskWithRunnable implements Runnable { public void run() { System.out.println("SubTaskWithRunnable started..."); } } 1.3. Using Lambda Expressions Lambda expressions help create inline instances of functional interfaces and can help reduce the boilerplate code. Runnable subTaskWithLambda = () -> { System.out.println("SubTaskWithLambda started..."); }; 2. Starting a New Thread We can start a new thread in Java in multiple ways, let us learn about them. 2.1. Using Thread.start() Thread’s start() method is considered the heart of multithreading. Without executing this method, we cannot start a new Thread. The other methods also internally use this method to start a thread, except virtual threads. The start() method is responsible for registering the thread with the platform thread scheduler and doing all the other mandatory activities such as resource allocations. Let us learn with an example how to create and start a Thread using start() method. class SubTask extends Thread { public void run() { System.out.println("SubTask started..."); } } Thread subTask = new SubTask(); subTask.start(); We can use the start() method to execute threads created using the Runnable interface. class SubTaskWithRunnable implements Runnable { public void run() { System.out.println("SubTaskWithRunnable started..."); } } Thread subTaskWithRunnable = new Thread(new SubTaskWithRunnable()); subTaskWithRunnable.start(); Using the lambda expression technique is no different from Runnable interface method. Runnable subTaskWithLambda = () -> { System.out.println("SubTaskWithLambda started..."); }; Thread subTask = new Thread(subTaskWithLambda); subTask.start(); 2.2. Using ExecutorService Creating a new Thread is resource intensive. So, creating a new Thread, for every subtask, decreases the performance of the application. To overcome these problems, we should use thread pools. A thread pool is a pool of already created threads ready to do our task. In Java, ExecutorService is the backbone of creating and managing thread pools. We can submit either a Runnable or a Callable task in the ExecutorService, and it executes the submitted task using the one of the threads in the pool. Runnable subTaskWithLambda = () -> { System.out.println("SubTaskWithLambda started..."); }; ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.execute(subTaskWithLambda); If we create the thread using lambda expression then the whole syntax becomes so easy. ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.execute(() -> { System.out.println("SubTaskWithLambda started..."); }); 2.3. Delayed Execution with ScheduledExecutorService When we submit a task to the executor, it executes as soon as possible. But suppose we want to execute a task after a certain amount of time or to run the task periodically, then we can use ScheduledExecutorService. The following example is scheduling a task to be executed after 1o seconds delay, and it will return the result “completed” when it has completed its execution. ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); ScheduledFuture<String> result = executor.schedule(() -> { System.out.println("Thread is executing the job"); return "completed"; }, 10, TimeUnit.SECONDS); System.out.println(result.get()); 2.4. Using CompletableFuture CompletableFuture is an extension to Future API introduced in Java 8. It implements Future and CompletionStage interfaces. It provides methods for creating, chaining and combining multiple Futures. In the following example, CompletableFuture will start a new Thread and executes the provided task either by using runAsync() or supplyAsync() methods on that thread. // Running RunnableJob using runAsync() method of CompletableFuture CompletableFuture<Void> future = CompletableFuture.runAsync(new RunnableJob()); // Running a task using supplyAsync() method of CompletableFuture and return the result CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> "Thread is executing"); // Getting result from CompletableFuture System.out.println(result.get()); Concurrency Java CallableCompletableFutureExecutorServiceRunnableScheduledExecutorService