Threading in Java

Threading is a technique in Java to allow multiple tasks to run concurrently (in parallel or in interleaved fashion).
A thread is a lightweight sub-process — the smallest unit of execution.

✅ Java provides built-in support for multithreading via the java.lang.Thread class and the Runnable interface.


🔄 Why Use Threads?

  • Improve performance via parallel execution
  • Perform background tasks (e.g., file download, animations)
  • Efficient use of CPU resources

🧩 Ways to Create a Thread

1. By Extending Thread Class

class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running...");
    }
}

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

 


2. By Implementing Runnable Interface

class MyTask implements Runnable {
    public void run() {
        System.out.println("Task is running...");
    }
}

public class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(new MyTask());
        t.start();  // starts the new thread
    }
}

 

⏱️ Thread Lifecycle

New → Runnable → Running → (Blocked/Waiting) → Terminated

🔧 Common Thread Methods

Method Description
start() Starts the thread
run() Code to execute in thread
sleep(ms) Pauses the thread
join() Waits for thread to finish
isAlive() Checks if thread is still running
setPriority() Sets thread priority (1 to 10)


public class Demo extends Thread {
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(i);
            try { Thread.sleep(1000); } catch (Exception e) {}
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Demo t1 = new Demo();
        Demo t2 = new Demo();

        t1.start();
        t1.join(); // wait for t1 to finish

        t2.start();
    }
}

🧠 Example with sleep() and join():

⚠️ Thread Safety & Concurrency

  • Multiple threads accessing shared data can cause race conditions
  • Use synchronization (synchronized keyword), locks, or concurrent APIs (ReentrantLock, AtomicInteger, etc.)

✅ Summary

Feature Description
Thread Lightweight process
Concurrency Multiple threads executing in parallel
Thread class Built-in support for creating threads
Runnable Preferred approach (decouples logic)
Use cases Background tasks, async processing

 

🧵 What is a Thread Pool?

A Thread Pool is a collection of pre-instantiated reusable threads managed by the Java runtime to execute multiple tasks efficiently.

✅ Instead of creating a new thread every time, Java reuses existing threads from the pool → improves performance, scalability, and resource management.


📦 Part of:

java.util.concurrent package


✅ Why Use a Thread Pool?

  • Reduces overhead of thread creation
  • Limits number of concurrent threads
  • Avoids resource exhaustion
  • Ideal for handling large numbers of short tasks

🔧 Creating a Thread Pool

Java provides the Executors utility class to create thread pools.

import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3); // Pool of 3 threads

        for (int i = 1; i <= 5; i++) {
            Runnable task = new MyTask("Task " + i);
            executor.execute(task);
        }

        executor.shutdown(); // Gracefully shutdown pool
    }
}

class MyTask implements Runnable {
    private String name;

    public MyTask(String name) {
        this.name = name;
    }

    public void run() {
        System.out.println(name + " is running on " + Thread.currentThread().getName());
    }
}


🛠️ Types of Thread Pools in Java:

Method Description
newFixedThreadPool(int n) Pool with fixed number of threads
newCachedThreadPool() Creates new threads as needed; reuses old ones
newSingleThreadExecutor() Only one thread executes tasks sequentially
newScheduledThreadPool(int n) Executes tasks with delay or periodically


🧠 Important Interfaces:

  • ExecutorService – interface to manage and control thread pool execution.
  • Key methods:
    • execute(Runnable r)
    • submit(Callable<T> c) – returns result (Future)
    • shutdown() – graceful shutdown
    • shutdownNow() – force shutdown
    • awaitTermination() – wait until all tasks complete

⚠️ Best Practices:

  • Always call shutdown() after use
  • Use submit() instead of execute() if return value is needed
  • Avoid using more threads than available CPU cores unless tasks are I/O bound

✅ Summary:

Feature Description
Purpose Reuse threads for efficient task execution
Key API Executors, ExecutorService
Types Fixed, Cached, Single, Scheduled
Use cases Web servers, batch processing, task queues

🧵 Callable and Future in Java Threading

In Java, Callable and Future are used to perform concurrent tasks that return a result or throw exceptions, unlike Runnable, which does neither.


🔄 1. Callable Interface

Callable<V> is a functional interface that:

  • Returns a result
  • Can throw checked exceptions

📦 Package: java.util.concurrent

🧪 Syntax:

Callable<String> task = () -> {
    Thread.sleep(1000);
    return "Hello from Callable";
};

⏳ 2. Future Interface

Future<V> represents the result of an asynchronous computation.

You get a Future object when you submit a Callable to an executor.
You can use it to:

  • Check if the task is done
  • Cancel the task
  • Retrieve the result

🔧 Example with ExecutorService

import java.util.concurrent.*;

public class CallableFutureExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        Callable<String> callableTask = () -> {
            Thread.sleep(2000);
            return "Task completed!";
        };

        Future<String> future = executor.submit(callableTask);

        System.out.println("Task submitted...");

        String result = future.get(); // blocks until result is ready
        System.out.println("Result: " + result);

        executor.shutdown();
    }
}

🧾 Output:

Task submitted...
Result: Task completed!

🆚 Callable vs Runnable

Feature Runnable Callable<V>
Returns value ❌ No ✅ Yes (V)
Throws checked exceptions ❌ No ✅ Yes
Used with Thread, ExecutorService ExecutorService

✅ Summary

Interface Purpose Key Method
Callable<V> Task that returns a result V call()
Future<V> Handle async result V get(), cancel()

⏰ What is Thread Scheduling in Java?

Scheduling threads means executing tasks:

  • After a delay
  • At fixed intervals (repeatedly)

Java provides the ScheduledExecutorService for scheduled and periodic task execution, replacing older Timer and TimerTask classes.

📦 Package: java.util.concurrent


✅ Key Interface: ScheduledExecutorService

You can get it via:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(int poolSize);


🔧 Common Scheduling Methods:

Method Description
schedule(Runnable task, delay, unit) Runs once after a delay
scheduleAtFixedRate(task, initDelay, period, unit) Repeats at fixed intervals, regardless of task duration
scheduleWithFixedDelay(task, initDelay, delay, unit) Waits for task to finish, then waits again before restarting

🧪 Example: One-Time Delayed Task

import java.util.concurrent.*;

public class DelayedTaskExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        Runnable task = () -> System.out.println("Executed after 3 seconds");

        scheduler.schedule(task, 3, TimeUnit.SECONDS);

        scheduler.shutdown();
    }
}

🧪 Example: Repeating Task (Fixed Rate)

public class FixedRateExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        Runnable task = () -> System.out.println("Running at: " + System.currentTimeMillis());

        scheduler.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);
    }
}

🕒 Starts after 1 second, then repeats every 2 seconds (regardless of how long the task takes).


🧪 Example: Repeating Task (Fixed Delay)

public class FixedDelayExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        Runnable task = () -> {
            System.out.println("Running at: " + System.currentTimeMillis());
            try { Thread.sleep(1000); } catch (Exception e) {}
        };

        scheduler.scheduleWithFixedDelay(task, 1, 2, TimeUnit.SECONDS);
    }
}

🕒 Starts after 1 second, then waits 2 seconds after task completes before running again.


⚠️ Tips:

  • Use shutdown() to stop gracefully
  • Use scheduleWithFixedDelay for variable-length tasks
  • Use scheduleAtFixedRate for time-sensitive periodic tasks

✅ Summary:

Feature ScheduledExecutorService
One-time delay schedule()
Repeating task scheduleAtFixedRate(), scheduleWithFixedDelay()
Thread-safe? ✅ Yes
Replacement for Timer and TimerTask

👻 What is a Daemon Thread?

A Daemon Thread in Java is a background thread that runs in support of user (non-daemon) threads.
It automatically terminates when all user threads have finished execution.

✅ Think of it like a helper thread (e.g., garbage collector, background logging).


✅ Key Characteristics:

Feature Description
Purpose Run background tasks
Lifetime Ends automatically when all user threads are done
Priority Usually low
Use cases GC, monitoring, background cleanup, scheduler
Converts to daemon Only before starting (setDaemon(true) must be called before start())

🔧 How to Create a Daemon Thread

🔹 Example:

public class DaemonExample extends Thread {
    public void run() {
        while (true) {
            System.out.println("Daemon thread is running...");
            try { Thread.sleep(1000); } catch (InterruptedException e) {}
        }
    }

    public static void main(String[] args) {
        DaemonExample t = new DaemonExample();
        t.setDaemon(true); // must be called before start()
        t.start();

        System.out.println("Main thread ends.");
    }
}

🔍 Output:

You may see the daemon output once or twice, but it will stop as soon as the main thread ends.


⚠️ Rules:

  • Must call setDaemon(true) before starting the thread
  • Daemon threads should not perform critical tasks
  • You cannot make the main thread a daemon

🧠 Daemon vs User Thread:

Property User Thread Daemon Thread
Execution ends? When run() completes When all user threads finish
Used for? Core business logic Background services
JVM waits to finish? ✅ Yes ❌ No (terminates automatically)
Created by default? ✅ Main thread is user ❌ Must be explicitly set

✅ Summary:

  • Daemon = background helper
  • Stops when no more user threads are running
  • Use for non-critical background work
  • Set with setDaemon(true) before start()

🎚️ What is Thread Priority?

In Java, thread priority helps the scheduler decide which thread to execute first when multiple threads are ready to run.
Each thread is assigned a priority between 1 (MIN_PRIORITY) and 10 (MAX_PRIORITY).

💡 Higher-priority threads are more likely to be selected for execution, but it's not guaranteed (depends on JVM & OS).


✅ Priority Constants (from Thread class):

Constant Value Description
Thread.MIN_PRIORITY 1 Lowest priority
Thread.NORM_PRIORITY 5 Default priority
Thread.MAX_PRIORITY 10 Highest priority

🔧 How to Set/Get Priority:

Thread t = new Thread();
t.setPriority(8); // set priority
System.out.println(t.getPriority()); // get priority

🧪 Example:

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

public class PriorityExample {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setPriority(Thread.MIN_PRIORITY);   // 1
        t2.setPriority(Thread.NORM_PRIORITY);  // 5 (default)
        t3.setPriority(Thread.MAX_PRIORITY);   // 10

        t1.start();
        t2.start();
        t3.start();
    }
}


⚠️ Important Notes:

  • Priority affects scheduling, not correctness.
  • JVM thread scheduler behavior can vary across platforms.
  • Threads with higher priority may not always run first.

📌 Summary:

Feature Value
Range 1 to 10
Default 5 (NORM_PRIORITY)
Effect Hints scheduler, no guarantee
Use-case When some threads are more important (e.g., UI vs background work)

 

👥 What is ThreadGroup in Java?

The ThreadGroup class is used to group multiple threads into a single unit.
This allows you to manage a group of threads together — like checking their status, interrupting them all at once, etc.

✅ Useful for organizing related threads and performing batch operations on them.


📦 Package:

java.lang.ThreadGroup


✅ Key Features:

Feature Description
Thread grouping You can manage multiple threads as a group
Hierarchy supported A ThreadGroup can have a parent group
Priority control You can set max priority for all threads in the group
Interrupt control You can interrupt all threads in the group at once
Thread management View count, enumerate active threads, etc.

🔧 Common Constructors:

ThreadGroup group = new ThreadGroup("MyGroup");
Thread t1 = new Thread(group, "Thread-1");

🧪 Example:

public class ThreadGroupDemo {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("MyGroup");

        Thread t1 = new Thread(group, () -> {
            System.out.println("Thread 1 running");
        });

        Thread t2 = new Thread(group, () -> {
            System.out.println("Thread 2 running");
        });

        t1.start();
        t2.start();

        System.out.println("Active threads in group: " + group.activeCount());
    }
}


🧰 Useful Methods:

Method Description
activeCount() Number of active threads
enumerate(Thread[] tArray) Copies threads into an array
interrupt() Interrupts all threads in the group
getName() Returns name of the group
getParent() Returns parent group
setMaxPriority(int priority) Sets max priority for new threads

⚠️ Limitations:

  • Rarely used in modern Java.
  • Thread management is better handled via Executors/ThreadPool in java.util.concurrent.

✅ Summary:

Feature Description
Class Name ThreadGroup
Purpose Group and manage multiple threads
Hierarchical? ✅ Supports parent-child structure
Modern use? ⚠️ Rare – replaced by Executor framework

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.