Multithreading in Java allows the concurrent execution of two or more threads, enabling the program to perform multiple tasks simultaneously. This section covers the basics of creating and managing threads, their lifecycle, synchronization, and inter-thread communication.
1. Creating Threads (Thread Class, Runnable Interface)
There are two main ways to create a thread in Java:
- By extending the
Thread
class - By implementing the
Runnable
interface
1.1 Extending the Thread Class
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running...");
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // Start the thread
}
}
1.2 Implementing the Runnable Interface
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable thread is running...");
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable);
t1.start(); // Start the thread
}
}
2. Thread Lifecycle
The lifecycle of a thread consists of several states:
- New: When a thread is created but not yet started.
- Runnable: After the
start()
method is invoked, the thread enters this state. - Blocked/Waiting: When a thread is waiting for resources or other threads.
- Timed Waiting: When a thread is waiting for a specific period.
- Terminated: When a thread completes its execution or is interrupted.
3. Synchronization
In a multithreading environment, synchronization ensures that only one thread can access a shared resource at a time to prevent data inconsistency. You can use the synchronized
keyword to achieve synchronization.
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
class MyThread extends Thread {
Counter counter;
MyThread(Counter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
MyThread t1 = new MyThread(counter);
MyThread t2 = new MyThread(counter);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + counter.getCount());
}
}
4. Inter-thread Communication
Inter-thread communication allows threads to communicate with each other via the wait()
, notify()
, and notifyAll()
methods. These methods are used in synchronization to make threads wait or notify them when a condition is met.
class SharedResource {
private boolean isDataReady = false;
public synchronized void produceData() throws InterruptedException {
while (isDataReady) {
wait();
}
System.out.println("Producing data...");
isDataReady = true;
notify();
}
public synchronized void consumeData() throws InterruptedException {
while (!isDataReady) {
wait();
}
System.out.println("Consuming data...");
isDataReady = false;
notify();
}
}
class Producer extends Thread {
SharedResource resource;
Producer(SharedResource resource) {
this.resource = resource;
}
public void run() {
try {
resource.produceData();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer extends Thread {
SharedResource resource;
Consumer(SharedResource resource) {
this.resource = resource;
}
public void run() {
try {
resource.consumeData();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
SharedResource resource = new SharedResource();
Producer producer = new Producer(resource);
Consumer consumer = new Consumer(resource);
producer.start();
consumer.start();
producer.join();
consumer.join();
}
}
Conclusion
Multithreading is a powerful feature of Java that allows you to perform multiple tasks simultaneously. It's especially useful in Selenium automation for running parallel tests, improving performance, and reducing execution time.