How to Create and Start a New Thread in Java

A Thread is a lightweight process that allows a program to operate more efficiently by running multiple threads in parallel. In this Java concurrency tutorial, we will learn to create and execute threads in different ways and their usecases.

1. Creating a New Thread

In Java, we can create a Thread in 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 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();

Eventually, Thread class start() method is the only way to start a new Thread in Java. The other ways (except virtaul threads) internally uses start() method.

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());

2.5. Executing as a Virtual Thread

Since Java 19, virtual threads have been added as JVM-managed lightweight threads that will help in writing high throughput concurrent applications.

We can run a task as a virtual thread using the newly added APIs:

Runnable runnable = () -> System.out.println("Inside Runnable");
Thread.startVirtualThread(runnable);

//or

Thread.startVirtualThread(() -> {
	//Code to execute in virtual thread
	System.out.println("Inside Runnable");
});

//or

Thread.Builder builder = Thread.ofVirtual().name("Virtual-Thread");
builder.start(runnable); 

3. Conclusion

We have learned the different ways of creating and starting a new Thread in java. We learned the methods of creating Thread class, Runnable interface, ExecutorService and even virtual threads.

Happy Learning !!

Sourcecode on Github

Leave a Reply

0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions, and frequently asked interview questions.