Why wait() and notify() Are on Object Class
Fast recall
Quick revision
Core answer
Conceptual understanding of Java's design decision — why these methods are on Object, not Thread.
Thinking wait() is on the Thread class because it affects thread behavior — it's on Object because it's about locks.
Explain the monitor ownership requirement and why it naturally belongs to Object — any object can be a lock.
Interview-ready guide
Interview answer
So wait, notify, and notifyAll are on the Object class, not the Thread class, and there's a really good reason for that.
In Java, every single object can act as a monitor, which is basically a lock that also has a waiting area attached to it. When you write synchronized on any object — could be a String, an ArrayList, or a custom object — you're using that object as a lock.
Since any object can be a lock, it makes sense that any object can also have threads waiting on it and waking up from it. That's why these methods are on Object. If they were on Thread, you'd be coordinating based on threads themselves, which doesn't match how synchronization works.
You want to coordinate around shared data, not around which thread is doing the work. Think about a producer-consumer setup. The queue object is what needs protection.
Threads wait on the queue when it's empty or full, and they notify on the queue when they add or remove items. The queue is the monitor. If wait was on Thread, you'd have to keep track of which threads are waiting for which resources, and it would be a mess.
This design also means you can have multiple independent locks in the same program. Like, you might have a customerLock and an orderLock. Threads waiting on customerLock are completely separate from threads waiting on orderLock.
When you call notifyAll on customerLock, it only wakes threads in that object's waiting set, not threads waiting on orderLock. One thing that trips up freshers is they synchronize on one object but accidentally call wait on a different object. That throws IllegalMonitorStateException because you don't own the monitor of the object you're waiting on.
The synchronized object and the wait object have to be the same. So in summary, wait and notify are on Object because objects are monitors in Java, and monitors are the things we coordinate on, not threads.
Why wait() and notify() Are on Object Class
Direct Answer
wait() and notify() are on the Object class because every Java object can serve as a monitor, which means an intrinsic lock. Since synchronization can be done on any object, the wait and notify mechanism must be available on every object too.
Code Example
class MultiResourceService {
private final Object customerLock = new Object();
private final Object orderLock = new Object();
private boolean customersReady = false;
private boolean ordersReady = false;
public void waitForCustomers() throws InterruptedException {
synchronized (customerLock) { // customerLock is the monitor
while (!customersReady) {
customerLock.wait(); // wait on customerLock's waiting set
}
}
}
public void waitForOrders() throws InterruptedException {
synchronized (orderLock) { // orderLock is a different monitor
while (!ordersReady) {
orderLock.wait(); // wait on orderLock's waiting set
}
}
// These two waiting sets are completely independent
}
public void markCustomersReady() {
synchronized (customerLock) {
customersReady = true;
customerLock.notifyAll(); // wakes only customerLock waiters
}
}
}
Common Mistakes & Pitfalls
- Thinking wait is on Thread: Freshers often look for Thread.wait() or currentThread.wait() because wait pauses the current thread. The confusion is understandable, but it's wrong — wait operates on the object's monitor, not on threads directly.
- Synchronizing on one object, waiting on another: Writing synchronized(lockA) then calling lockB.wait() seems like it might work, but it throws IllegalMonitorStateException because you don't own lockB's monitor. The synchronized object and the wait object must match.
- Using the this keyword carelessly: In a synchronized method, this is the implicit monitor. If you later refactor to use a different lock object for better concurrency, you might forget to update wait() calls, breaking the code.
Check your understanding
Answer these without looking back at the article.
- Can you call wait() on a String literal?
- What is the relationship between synchronized and intrinsic locks?
- How does ReentrantLock and Condition provide a more flexible alternative?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.