Overview:
Multi-threading is a feature in Java that allows concurrent execution of two or more parts of a program to maximize CPU utilization. Each part of a program that runs in parallel is called a thread, and threads can run independently.
Basics of Multi-Threading
Creating Threads:
There are two main ways to create a thread in Java:
- Extending the Thread class.
- Implementing the Runnable interface.
Extending Thread Class:
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // Start the thread
}
}
Implementing Runnable Interface:
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running");
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start(); // Start the thread
}
}
Thread Lifecycle:
A thread can be in one of the following states:
- New: When a thread is created but not yet started.
- Runnable: After calling the start() method, the thread is ready to run when the CPU is available.
- Blocked: When a thread is waiting for a monitor lock.
- Waiting: When a thread is waiting indefinitely for another thread to perform a particular action.
- Timed Waiting: When a thread is waiting for another thread to perform an action within a stipulated amount of time.
- Terminated: When a thread has finished its execution.
Synchronization
Why Synchronization?
Synchronization is essential to prevent thread interference and memory consistency errors. When multiple threads try to access shared resources simultaneously, it can lead to data corruption.
Synchronized Method:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SyncExample {
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count: " + counter.getCount()); // Output: 2000
}
}
Synchronized Block:
class Counter {
private int count = 0;
public void increment() {
synchronized (this) {
count++;
}
}
public int getCount() {
return count;
}
}
Conclusion
Multi-threading and asynchronous programming are essential concepts in Java that help improve application performance and responsiveness. Understanding how to create and manage threads, synchronize resources, and utilize Future and CompletableFuture for asynchronous tasks is crucial for developing efficient Java applications. With these tools and techniques, you can write scalable, high-performance code that leverages the full power of modern multi-core processors.