Java
multithreading
concurrency
method invocation
programming tutorial

How to call a method with a separate thread in Java?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Java is a powerful, versatile language that offers multiple ways to handle concurrent programming. One of the most common patterns in concurrent programming is executing tasks in separate threads. This approach increases the efficiency and responsiveness of an application by executing non-dependent tasks concurrently. Here, we'll explore how to call a method using a separate thread in Java, including technical examples as well as best practices for optimal performance.

Thread Basics in Java

In Java, a thread is the smallest unit of processing. Two main ways to create a thread are:

  1. Extending the Thread class: By creating a subclass of Thread.
  2. Implementing the Runnable interface: By creating a class that implements Runnable.

Extending the Thread Class

java
1class MyThread extends Thread {
2    @Override
3    public void run() {
4        // Method logic here
5        System.out.println("MyThread is running");
6    }
7}
8
9// To start the thread
10MyThread thread = new MyThread();
11thread.start();

Implementing the Runnable Interface

java
1class MyRunnable implements Runnable {
2    @Override
3    public void run() {
4        // Method logic here
5        System.out.println("MyRunnable is running");
6    }
7}
8
9// To start the thread
10Thread thread = new Thread(new MyRunnable());
11thread.start();

Using Lambda Expressions (Java 8+)

For brevity, Java 8 introduced lambda expressions that can be used to create Runnable instances:

java
1Thread thread = new Thread(() -> {
2    // Method logic here
3    System.out.println("Runnable with Lambda is running");
4});
5thread.start();

Synchronization and Concurrency Control

When dealing with threads, managing shared resources is critical. Java provides the synchronized keyword to prevent multiple threads from accessing a shared resource simultaneously.

java
public synchronized void synchronizedMethod() {
    // Critical section of the code
}

Alternatively, Java's ReentrantLock provides an extended locking mechanism.

java
1import java.util.concurrent.locks.ReentrantLock;
2
3ReentrantLock lock = new ReentrantLock();
4
5public void safeMethod() {
6    lock.lock();
7    try {
8        // Critical section of the code
9    } finally {
10        lock.unlock();
11    }
12}

Executors Framework

Java's Executors framework is a higher-level API to manage thread execution by handling the creation and management of thread pools, which is more efficient than manually creating threads.

java
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3
4ExecutorService executorService = Executors.newFixedThreadPool(2);
5executorService.submit(() -> {
6    // Method logic here
7    System.out.println("Executor Service is running");
8});
9executorService.shutdown();

Thread Pool Executors Options

  • FixedThreadPool: A pool with a fixed number of threads, which remains constant.
  • CachedThreadPool: Dynamically creates new threads and can reuse previously constructed threads.
  • SingleThreadExecutor: Ensures that tasks are executed sequentially by a single thread.

Future and Callable

Java introduces Callable and Future in the java.util.concurrent package for threads that need to return a result.

java
1import java.util.concurrent.Callable;
2import java.util.concurrent.ExecutionException;
3import java.util.concurrent.ExecutorService;
4import java.util.concurrent.Executors;
5import java.util.concurrent.Future;
6
7Callable<Integer> callableTask = () -> {
8    // Task logic
9    return 42;
10};
11
12ExecutorService executorService = Executors.newSingleThreadExecutor();
13Future<Integer> future = executorService.submit(callableTask);
14try {
15    Integer result = future.get(); // blocks until the task is complete
16    System.out.println("Callable result: " + result);
17} catch (InterruptedException | ExecutionException e) {
18    e.printStackTrace();
19}
20executorService.shutdown();

Key Considerations in Thread Management

AspectDetailsBest Practice
Thread CreationThread, Runnable, Executors frameworkPrefer Executors for complex applications
SynchronizationPrevent race condition using synchronizedUse concise locking, consider ReentrantLock
Thread SafetyEnsure shared data is accessed safelyUse immutable objects or thread-safe collections
Thread Pool ManagementOptimize resource usageChoose appropriate pool size and type
Error HandlingHandle InterruptedException gracefullyEnsure proper thread interruption and cleanup

Conclusion

Executing methods in separate threads in Java is a highly beneficial way to improve the efficiency and responsiveness of applications. By understanding the different approaches and best practices, developers can effectively leverage Java's concurrency features to create robust and performant applications. Whether you choose to extend the Thread class, implement Runnable, or use higher-level constructs like the Executors Framework, careful management of threads and resources is key to maintaining a stable and efficient application.


Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.