In Java, wait
and notify
are key methods used in object manipulation within multithreaded programs. When you have multiple threads, synchronization is often necessary to maintain data consistency and integrity. The wait
method allows a thread to temporarily release the lock it holds on an object and essentially go to sleep until another thread calls notify
.
How it Works
To put this into perspective, imagine multiple roommates sharing a washing machine. If one person (thread) is using it, others must wait for their turn. Similarly, in Java, when a thread needs to access a resource that another thread is using, it can call wait
to release its hold and suspend its execution. When the resource is free, another thread can call notify
to restart the waiting thread's activity.
It's crucial to note that both wait
and notify
must be called from within a synchronized context. This requirement ensures that threads have appropriate access control when interacting with shared resources.
Using Wait and Notify in Java
Let's dive into how you can implement these methods effectively.
Synchronized Block and Wait
Here's a simple way to use wait
within a synchronized block.
class Library {
public synchronized void requestBook() {
try {
System.out.println("Waiting to request a book...");
wait(); // Release the lock and wait
System.out.println("Book request processed.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Explanation:
- synchronized: Ensures only one thread can access the method at a time.
- wait(): Causes the current thread to wait until another thread invokes
notify
on the same object.
Synchronized Block and Notify
Having a wait
without a notify
is like setting an alarm but never waking up.
class Library {
public synchronized void returnBook() {
System.out.println("Returning a book.");
notify(); // Wake up waiting thread
}
}
Explanation:
- notify(): Wakes up a single thread that is waiting on this object's monitor.
Practical Implementation
Here's a practical example that ties it all together.
class BookProcessor {
private final Library library = new Library();
public void processRequest() throws InterruptedException {
Thread requester = new Thread(library::requestBook);
Thread returner = new Thread(library::returnBook);
requester.start();
Thread.sleep(1000); // Ensure requestBook starts first
returner.start();
}
}
Explanation:
- Thread Class: Threads are created for each action.
- start(): Begins thread execution.
- Thread.sleep(): Ensures proper execution order.
Handling InterruptedException
wait()
throws InterruptedException
, so it's essential to handle it properly.
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Preserve interrupt status
System.out.println("Thread was interrupted");
}
Explanation:
- catch block: Handles the exception.
- Thread.currentThread().interrupt(): Restores interrupt status after handling interruption.
Timed Wait
Sometimes, you might want a timed wait. That's possible with wait(long timeout)
.
public synchronized void timedWait() {
try {
System.out.println("Timed wait of 500ms");
wait(500); // Wait for 500 milliseconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Explanation:
- wait(long timeout): Stops waiting after the given time elapses if
notify
isn't called.
Conclusion
Understanding wait
and notify
is essential for effective multithreading in Java. These methods let threads communicate about changes in object states while maintaining data integrity. Start experimenting with these methods in your code to see how they can optimize thread synchronization and performance. For more on Java concurrency, check out Understanding Concurrency and Multithreading - The Code. Happy coding!