Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Java Multithreading

Thread Lifecycle

StateDescription
NEWThread created but start() not yet called.
RUNNABLEThread is ready to run or currently running.
BLOCKEDWaiting to acquire a monitor lock.
WAITINGWaiting indefinitely for another thread (e.g., wait()).
TIMED_WAITINGWaiting for a specified time (e.g., sleep(), join(timeout)).
TERMINATEDThread has finished execution.

Creating Threads

extends Thread vs implements Runnable
// Approach 1: extend Thread
class CounterThread extends Thread {
    private String name;
    public CounterThread(String name) { this.name = name; }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(name + ": " + i);
            try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        }
    }
}

// Approach 2: implement Runnable (preferred — allows extending another class)
class PrintTask implements Runnable {
    private String message;
    public PrintTask(String message) { this.message = message; }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + message);
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        // Approach 1
        CounterThread t1 = new CounterThread("Thread-A");
        CounterThread t2 = new CounterThread("Thread-B");
        t1.start();
        t2.start();
        t1.join();  // wait for t1 to finish
        t2.join();

        // Approach 2
        Thread t3 = new Thread(new PrintTask("Hello"), "Worker-1");
        Thread t4 = new Thread(() -> System.out.println("Lambda thread!"), "Worker-2");
        t3.start();
        t4.start();
    }
}

synchronized & wait/notify

synchronized & ExecutorService
import java.util.concurrent.*;

class Counter {
    private int count = 0;

    // synchronized — only one thread can execute this at a time
    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

public class SyncDemo {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        // Create 10 threads, each incrementing 1000 times
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) counter.increment();
            });
            threads[i].start();
        }
        for (Thread t : threads) t.join();
        System.out.println("Final count: " + counter.getCount());  // 10000

        // ExecutorService — thread pool (preferred over raw threads)
        ExecutorService executor = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 8; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " on " + Thread.currentThread().getName());
            });
        }
        executor.shutdown();
        executor.awaitTermination(5, TimeUnit.SECONDS);
    }
}

Key Takeaways
  • A thread is the smallest unit of execution. Java supports multithreading via Thread class and Runnable interface.
  • Extend Thread or implement Runnable — prefer Runnable as Java does not support multiple inheritance.
  • Use synchronized keyword to prevent race conditions when multiple threads access shared data.
  • ExecutorService manages a thread pool — more efficient than creating new threads manually.
  • volatile keyword ensures visibility of variable changes across threads — but does not ensure atomicity.
  • Use AtomicInteger, AtomicLong for thread-safe counter operations without synchronized blocks.
  • Deadlock occurs when two threads wait for each other — always acquire locks in the same order.
Common Mistakes to Avoid
WRONG new Thread(task).start() in a loop
RIGHT ExecutorService pool = Executors.newFixedThreadPool(4)
Creating new threads for every task is expensive. Use a thread pool to reuse threads and control concurrency.
WRONG int count = 0; count++ in multiple threads
RIGHT AtomicInteger count = new AtomicInteger(0); count.incrementAndGet()
count++ is not atomic — it is read-modify-write. Use AtomicInteger or synchronized for thread-safe counters.

Frequently Asked Questions

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.