Inter-Thread Communication in Java

Inter-Thread Communication in Java

In the world of Java multithreading, inter-thread communication is a critical concept. Threads often need to coordinate with each other to ensure smooth execution. While it may sound complex, a simple analogy can make it crystal clear. Let's explore inter-thread communication using the example of a worker and a wrench.

In Java, the methods wait(), notify(), and notifyAll() are the building blocks for inter-thread communication. They are used to synchronize and coordinate threads that share resources.

wait():

  • The wait() method causes the current thread to release the monitor lock and enter a waiting state.
  • The thread remains in this state until another thread calls notify() or notifyAll() on the same object.
  • Example: The worker puts the wrench down and waits until another worker signals that it’s time to use the wrench again.

notify():

  • The notify() method wakes up one thread waiting on the monitor lock of the object.
  • If multiple threads are waiting, one is selected arbitrarily (not guaranteed to be the first one to wait).
  • Example: A worker finishes their task and signals one specific waiting worker to pick up the wrench and proceed.

notifyAll():

  • The notifyAll() method wakes up all threads waiting on the monitor lock of the object.
  • Once notified, threads compete for the lock, and one of them gets to resume execution.
  • Example: The supervisor announces that all workers waiting for the wrench can start, and they must now coordinate among themselves.


Understanding the Process of Inter-Thread Communication

1. wait(): The Worker Waits for the Wrench

A worker approaches the shared toolbox but finds that the wrench is already in use. Instead of waiting idly, the worker decides to "pause" and steps aside, signaling that they are ready to use the wrench once it becomes available. This is equivalent to a thread calling the wait() method—it releases the lock and waits for a signal to proceed.

2. notify(): The Wrench is Now Available

When the worker currently using the wrench completes their task, they signal the next waiting worker that the wrench is now available. This is akin to a thread calling the notify() method, waking up one waiting thread to resume execution.

3. notifyAll(): All Workers Get Notified

In certain situations, the supervisor might decide to notify all the waiting workers that they can now access the wrench. This happens when the notifyAll() method is used, waking up all waiting threads. They then compete for the wrench, ensuring no one is left out of the process.

Synchronization Ensures Order

The toolbox, in this example, represents the shared resource, and it is protected by a lock (using the synchronized keyword in Java). This ensures that only one worker (thread) can access the wrench (resource) at any given time, preventing conflicts or misuse.


Why Are wait(), notify(), and notifyAll() Defined in the Object Class Instead of the Thread Class?

These methods are defined in the Object class because they work on the intrinsic lock of an object, not a thread. In Java, every object has a built-in lock that threads can synchronize on. Since wait(), notify(), and notifyAll() are used for communication between threads about the state of an object (e.g., resource availability), they logically belong to the Object class, where the lock resides.

Defining these methods in the Object class ensures any object can be used as a shared resource for thread synchronization, making the design flexible and consistent.

class Wrench {
    private boolean isAvailable = false;

    // Method to take the wrench
public synchronized void takeWrench(String workerName) throws InterruptedException {
        while (!isAvailable) { // If the wrench is unavailable, the thread waits.
            System.out.println(workerName + " is waiting for the wrench.");
            wait(); // Waits until notified by another thread.
        }
        isAvailable = false; // Wrench is now taken.
        System.out.println(workerName + " took the wrench.");
    }

    // Method to return the wrench
    public synchronized void returnWrench(String workerName) {
        isAvailable = true; // Wrench is now available.
        System.out.println(workerName + " returned the wrench.");
        notify(); // Notify one thread waiting for the wrench.
    }
}

class Worker extends Thread {
    private final Wrench wrench;
    private final String name;

    public Worker(Wrench wrench, String name) {
        this.wrench = wrench;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            wrench.takeWrench(name); // Try to take the wrench.
            Thread.sleep(1000); // Simulate work with the wrench.
            wrench.returnWrench(name); // Return the wrench after use.
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

public class FactorySimulation {
    public static void main(String[] args) {
        Wrench wrench = new Wrench();
        Worker worker1 = new Worker(wrench, "Worker 1");
        Worker worker2 = new Worker(wrench, "Worker 2");
        Worker worker3 = new Worker(wrench, "Worker 3");

        worker1.start();
        worker2.start();
        worker3.start();
    }
}        
Output -
Worker 1 is waiting for the wrench.
Worker 2 is waiting for the wrench.
Worker 1 took the wrench.
Worker 1 returned the wrench.
Worker 2 took the wrench.
Worker 2 returned the wrench.
Worker 3 took the wrench.
Worker 3 returned the wrench.        

要查看或添加评论,请登录

Tushar Kumar的更多文章

社区洞察

其他会员也浏览了