🚀 Java Deep Diving! Using Java Threads (Runnable, Thread, CompletableFuture, Tasks, Lambda Threads)

🚀 Java Deep Diving! Using Java Threads (Runnable, Thread, CompletableFuture, Tasks, Lambda Threads)

Here I will give a good explanation and practical examples about Threads in Java, talking about:

✅ Base concepts of Threads

✅ Using Runnable, Threads and Executors

✅ Main methods, start(), run(), join(), interrupt()...

✅ Advanced use with CompletableFuture and Lambda Threads

1️⃣ What are Threads in Java?

A thread is an independent execution flow into a program.

Java supports concurrent program, where many threads can be executed simultaneouslly

Threads are usable in parallel tasks, for example I/O, data processing and intensive calculations.

2️⃣ Creating an Java Thread

There are many ways to create a Thread.

✅ Using Thread Class (Extending)

class MyThread extends Thread {
 public void run() {
        System.out.println("Executing Thread: " + Thread.currentThread().getName());
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // Start the thread
    }
}        

🔹 Explanation:

We created an class MyThread that extends  (Inherit) Thread

We override the method run, to define a task

We called start() to initiate the Thread correctly

✅ Using Runnable Interface (Best Practice)

class MyTask implements Runnable {
    public void run() {
        System.out.println("Executing Thread: " + Thread.currentThread().getName());
    }
}
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyTask());
        thread.start();
    }
}        

🔹 Explanation:

Runnable gives the way to create threads without inherit

The code in more flexible and reusable

✅ Using Lambda Threads (Java 8+)

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> System.out.println("Executing Thread: " + Thread.currentThread().getName()));
        thread.start();
    }
}        

🔹 Explanation:

Here we utilize a lambda expression to create a Thread

3️⃣ Essentials methods of a Thread

start()

  • Start a Thread execution. Calls run internally

run()

  • Defines the Thread logic. If direct called, doesn't create a new Thread

join()

  • Makes the current thread wait until other thread finishes before continue

interrupt()

  • Interrupts a Thread, signs that it must stop.

isInterrupted()

  • Returns true if the Thread was interrupted

✅ Exemple using join()

class MyThread extends Thread {
    public void run() {
        try {
            Thread.sleep(3000);
            System.out.println("Thread finalizada!");
        } catch (InterruptedException e) {
            System.out.println("Thread interrompida!");
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();
        
        thread.join(); // Wait the thread finishes before continue
        
        System.out.println("Execution finished after  thread conclude.");
    }
}        

🔹 Explanation:

join() forces the main Thread to wait for the secondary Thread finish.

✅ Example of interrupt()

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Executing...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("Thread interrupted!");
                    return;
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(3000);
            thread.interrupt(); // Thread interrupted
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}        

🔹 Explanation:

interrupt() signals that a Thread must stop and cause an InterruptedException if the Thread is sleeping.

4️⃣ Using ExecutorService to manage Threads

✅ Creating an FixedThreadPool

import java.util.concurrent.*;
public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Runnable task = () -> System.out.println("Executing Thread: " + Thread.currentThread().getName());
        executor.submit(task);
        executor.submit(task);
        executor.shutdown(); // Finishes of Executor
    }
}        

🔹 Explanation:

The ExecutorService manage the lifecycle of Threads

newFixedThreadPool(2) creates an pool with 2 fixed Threads

5️⃣ Using CompletableFuture for Async Program

import java.util.concurrent.*;
public class Main {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Task completed!";
        });
        future.thenAccept(System.out::println); // Executes when Thread finishes
        System.out.println("Executing other operation...");
    }
}        

🔹 Explanation:

supplyAsync(), executes a task in background

thenAccept(), process the result when the task finishes

Doesn't block the main execution

6️⃣ Difference between Thread, ExecutorService and CompletableFuture

Creation

  • Thread is manual, instantiating as new Thread()
  • ExecutorService is managed, using Executors
  • CompletableFuture is Async, using supplyAsync()

Management

  • Thread, no management, needs to call .start()
  • ExecutorService, automatically manage the Threads
  • CompletableFuture, execute tasks with no blocks

Scalability

  • Thread is hard, needs multiple Threads
  • ExecutorService is controlled with pools of Threads
  • CompletableFuture doesn't create multiple Threads unnecessary

🚀 Summary

✅ Uses Thread and Runnable for manual management, but choose ExecutorService for scalability

✅ For async operations with no block, CompletableFuture is the best choice

✅ Avoid Thread.sleep() and choose ExecutorService to tasks schedules

To view or add a comment, sign in

More articles by Marcelo Honorio Santos

Others also viewed

Explore content categories