Java - Thread
A thread in Java is a lightweight process that allows for concurrent execution of code. Java provides built-in support for multi-threading, which enables developers to write programs that can perform multiple tasks simultaneously. Multi-threading is particularly useful for improving the performance of applications, especially when dealing with I/O-bound or CPU-bound tasks.
What is a Thread?
A thread is essentially a single path of execution through a program. A program can have multiple threads, each of which executes independently. Each thread in a Java program shares the same memory space and resources but has its own program counter, stack, and local variables.
Creating a Thread in Java
There are two primary ways to create a thread in Java:
By Extending the Thread class
By Implementing the Runnable interface
1. Extending the Thread class
In this approach, you can extend the Thread class and override the run() method.
Example:
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // Start the thread
}
}
In this example, MyThread extends the Thread class and overrides the run() method. The start() method is called to begin the thread's execution.
2. Implementing the Runnable interface
Alternatively, you can implement the Runnable interface and pass it to a Thread object.
Example:
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running using Runnable");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
t1.start(); // Start the thread
}
}
Here, the run() method is implemented in the MyRunnable class, and the Thread object is created by passing the Runnable instance to the Thread constructor.
Thread Lifecycle
A thread in Java goes through several states during its lifetime, which are managed by the Thread Scheduler:
New: The thread is created but not started yet.
Runnable: The thread is ready to run and is waiting for the CPU to allocate resources.
Blocked: The thread is waiting to acquire a lock or resource.
Waiting: The thread is waiting indefinitely for another thread to perform a particular action.
Timed Waiting: The thread is waiting for a specified period.
Terminated: The thread has completed execution.
Thread Methods
start(): Begins the execution of the thread.
run(): Contains the code to be executed by the thread.
sleep(long milliseconds): Causes the current thread to sleep for a specified time.
Thread.sleep(1000); // Sleep for 1 second
join(): Causes the current thread to wait until the thread being called finishes its execution.
t1.join(); // Waits for t1 to complete before continuing
setPriority(int priority): Sets the priority of a thread. A thread with higher priority gets more CPU time.
getPriority(): Gets the current priority of the thread.
isAlive(): Checks if the thread is still running.
Thread Synchronization
In a multi-threaded environment, multiple threads might attempt to access shared resources concurrently, leading to race conditions. To avoid these issues, synchronization is used to ensure that only one thread accesses a resource at a time.
Example of Synchronization:
class Counter {
private int count = 0;
// Synchronized method to ensure only one thread updates count at a time
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class ThreadExample {
public static void main(String[] args) {
Counter counter = new Counter();
// Creating two threads
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());
}
}
In this example, the increment() method is synchronized to prevent race conditions. Both threads attempt to increment the count, but due to synchronization, the count remains accurate.
Thread Pool
Java provides a ThreadPool to manage a collection of worker threads. It helps in reusing existing threads instead of creating new ones for every task, improving performance and resource management.
You can create a thread pool using the ExecutorService interface:
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3); // Create a pool of 3 threads
for (int i = 0; i < 5; i++) {
executor.submit(() -> {
System.out.println(Thread.currentThread().getName() + " is executing");
});
}
executor.shutdown(); // Shutdown the thread pool
}
}
The ExecutorService manages a pool of threads, which can be reused for different tasks.
Conclusion
Threads in Java provide a powerful mechanism for performing multiple operations concurrently. Java's built-in support for multi-threading, thread synchronization, and thread pooling makes it easier to build efficient, concurrent applications. By understanding how threads work and applying them correctly, you can significantly improve the performance and responsiveness of your applications.