Multithreading is a core feature of Java that allows a program to execute multiple tasks concurrently. Each task runs in a separate thread but shares the same process memory. Multithreading improves performance, responsiveness, and resource utilization, especially in applications like web servers, games, banking systems, and real-time systems.
A thread goes through different states during its execution. This sequence is called the thread lifecycle.
New → Runnable → Running → Waiting/Blocked → Runnable → Terminated
class Demo extends Thread {
public void run() {
System.out.println("Thread is running");
}
public static void main(String[] args) {
Demo t = new Demo(); // New
t.start(); // Runnable
}
}
Java provides two main ways to create threads.
run() methodclass MyThread extends Thread {
public void run() {
System.out.println("Thread running using Thread class");
}
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread running using Runnable");
}
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
| Thread | Runnable |
|---|---|
| Extends class | Implements interface |
| No multiple inheritance | Supports multiple inheritance |
| Less flexible | More preferred |
Starts a new thread and invokes run() internally.
t.start();
Pauses thread execution for a specified time.
Thread.sleep(1000); // 1 second
Makes one thread wait until another thread finishes.
t.join();
Interrupts a sleeping or waiting thread.
t.interrupt();
class Test extends Thread {
public void run() {
try {
Thread.sleep(500);
System.out.println("Thread executed");
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
}
public static void main(String[] args) throws Exception {
Test t = new Test();
t.start();
t.join();
System.out.println("Main thread ends");
}
}
Synchronization ensures that only one thread accesses shared resources at a time, preventing data inconsistency.
class Counter {
int count = 0;
synchronized void increment() {
count++;
}
}
synchronized(this) {
count++;
}
Threads communicate using:
wait()notify()notifyAll()These methods are defined in Object class.
class Chat {
synchronized void question(String msg) {
System.out.println(msg);
notify();
try {
wait();
} catch (Exception e) {}
}
synchronized void answer(String msg) {
System.out.println(msg);
notify();
try {
wait();
} catch (Exception e) {}
}
}
ExecutorService manages a pool of threads and executes tasks efficiently without creating threads manually.
import java.util.concurrent.*;
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(() -> {
System.out.println("Task executed by thread pool");
});
executor.shutdown();
}
}
Callable<Integer> task = () -> 10 + 20;
import java.util.concurrent.*;
public class CallableDemo {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Integer> task = () -> {
Thread.sleep(500);
return 100;
};
Future<Integer> future = executor.submit(task);
System.out.println("Result: " + future.get());
executor.shutdown();
}
}
| Runnable | Callable |
|---|---|
| No return value | Returns value |
| Cannot throw checked exception | Can throw exception |
| run() | call() |
java
// Method 1: Extending Thread class
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(name + " - Count: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// Method 2: Implementing Runnable
class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(name + " - Count: " + i);
}
}
}
// Synchronization example
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class MultithreadingDemo {
public static void main(String[] args) throws InterruptedException {
// Creating threads
MyThread t1 = new MyThread("Thread-1");
MyThread t2 = new MyThread("Thread-2");
t1.start();
t2.start();
// Using Runnable
Thread t3 = new Thread(new MyRunnable("Runnable-1"));
t3.start();
// Lambda expression with Runnable
Thread t4 = new Thread(() -> {
System.out.println("Lambda thread executing");
});
t4.start();
// Join example
t1.join(); // Wait for t1 to complete
System.out.println("Thread-1 has finished");
// Synchronization demo
Counter counter = new Counter();
Thread inc1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread inc2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
inc1.start();
inc2.start();
inc1.join();
inc2.join();
System.out.println("Final count: " + counter.getCount());
}
}