In Java, a thread is a lightweight subprocess that can execute concurrently with other parts of a program. Threads are widely used for performing multiple tasks simultaneously, such as handling user input, network communication, and parallel processing. This article explores how to create and manage threads in Java, offering insights into thread creation, synchronization, and thread pooling.
There are two main approaches to create threads in Java:
To create a thread by extending the Thread class, follow these steps:
Thread
class.run()
method, which contains the code to be executed in the thread.start()
method.Here's an example of creating a thread by extending the Thread
class:
class MyThread extends Thread {
public void run() {
// Code to be executed in the thread
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
To create a thread by implementing the Runnable
interface, follow these steps:
Runnable
interface.run()
method, which contains the code to be executed in the thread.Thread
object.start()
method.Here's an example of creating a thread by implementing the Runnable
interface:
class MyRunnable implements Runnable {
public void run() {
// Code to be executed in the thread
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
In multithreaded applications, synchronization is essential to ensure thread safety and avoid race conditions. Java provides synchronization mechanisms such as the synchronized
keyword and wait()
/notify()
methods.
The synchronized
keyword is used to create synchronized blocks or methods. It ensures that only one thread can access the synchronized block or method at a time.
class MyThread extends Thread {
private static int counter = 0;
public synchronized void run() {
// Synchronized block
counter++;
System.out.println("Counter: " + counter);
}
}
public class Main {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
// Both threads will execute the synchronized run method separately
thread1.start();
thread2.start();
}
}
The wait()
and notify()
methods are used to implement inter-thread communication, allowing threads to pause and resume execution based on certain conditions.
class MyThread extends Thread {
public static final Object lock = new Object();
public void run() {
synchronized (lock) {
System.out.println("Thread is waiting");
try {
lock.wait(); // Thread waits until notified
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread is notified and resumed");
}
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
synchronized (MyThread.lock) {
try {
Thread.sleep(2000); // Delay to ensure thread starts waiting
MyThread.lock.notify(); // Notify the waiting thread
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Creating a new thread for each task can be resource-intensive in situations with numerous short-lived tasks. Thread pooling addresses this issue by reusing existing threads from a pool, minimizing the overhead of creating new threads.
The Executor framework provides the ExecutorService
interface and its implementations, such as ThreadPoolExecutor
. Here's an example:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Runnable worker = new MyRunnable();
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated()) {
// Wait for all tasks to finish
}
System.out.println("All tasks completed");
}
}
In this example, we create a thread pool with a fixed number of threads (5). We submit Runnable
tasks to the executor, which handles thread allocation and task execution. Finally, we wait for all tasks to complete and shutdown the executor.
Threads play a crucial role in writing concurrent and efficient programs. By understanding how to create and manage threads in Java, utilizing synchronization mechanisms, and implementing thread pooling, developers can leverage the power of concurrent processing while maintaining thread safety and performance. Remember to use threads judiciously, as excessive or inefficient thread usage can lead to resource contention and other concurrency-related issues.
noob to master © copyleft