4.1.4 Thread Communication Explained
Thread communication in Java is essential for coordinating the activities of multiple threads to ensure they work together efficiently. Effective thread communication prevents race conditions and deadlocks, ensuring that threads can share resources and data without conflicts.
Key Concepts
1. wait(), notify(), and notifyAll()
These methods are used to facilitate communication between threads. The wait()
method causes the current thread to wait until another thread invokes the notify()
or notifyAll()
method for this object. The notify()
method wakes up a single waiting thread, while notifyAll()
wakes up all waiting threads.
Example
class SharedResource { private boolean condition = false; public synchronized void waitForCondition() throws InterruptedException { while (!condition) { wait(); } System.out.println("Condition met, proceeding..."); } public synchronized void setCondition() { condition = true; notifyAll(); } } public class Main { public static void main(String[] args) { SharedResource resource = new SharedResource(); Thread t1 = new Thread(() -> { try { resource.waitForCondition(); } catch (InterruptedException e) { e.printStackTrace(); } }); Thread t2 = new Thread(() -> { resource.setCondition(); }); t1.start(); t2.start(); } }
2. join()
The join()
method allows one thread to wait for the completion of another. When a thread calls the join()
method on another thread, it will wait until the other thread terminates.
Example
public class Main { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { System.out.println("Thread 1 is running"); }); Thread t2 = new Thread(() -> { System.out.println("Thread 2 is running"); }); t1.start(); t1.join(); // Main thread waits for t1 to finish t2.start(); t2.join(); // Main thread waits for t2 to finish System.out.println("Both threads have finished"); } }
3. volatile Keyword
The volatile
keyword ensures that changes to a variable are visible to all threads. It prevents the compiler from optimizing the variable and ensures that each thread reads the most recent value of the variable from main memory.
Example
class SharedResource { private volatile boolean condition = false; public void setCondition(boolean value) { condition = value; } public boolean getCondition() { return condition; } } public class Main { public static void main(String[] args) { SharedResource resource = new SharedResource(); Thread t1 = new Thread(() -> { while (!resource.getCondition()) { // Busy wait } System.out.println("Condition met, proceeding..."); }); Thread t2 = new Thread(() -> { resource.setCondition(true); }); t1.start(); t2.start(); } }
Examples and Analogies
Think of thread communication as a conversation between workers in a factory. The wait()
and notify()
methods are like a worker pausing their task until they receive a signal (notify) from another worker that it's safe to proceed. The join()
method is like a supervisor waiting for all workers to finish their tasks before moving on to the next phase. The volatile
keyword ensures that all workers have the most up-to-date information, preventing misunderstandings.
By mastering these thread communication techniques, you can develop robust and efficient Java applications that leverage the power of concurrent execution.