How can the wait() and notify() methods be called on Objects that are not threads?

JavaMultithreadingWaitNotify

Java Problem Overview


How can the wait() and notify() methods be called on Objects that are not Threads? That doesn't really make sense, does it?

Surely, it must make sense, however, because the two methods are available for all Java objects. Can someone provide an explanation? I am having trouble understanding how to communicate between threads using wait() and notify().

Java Solutions


Solution 1 - Java

Locking is about protecting shared data.

The lock is on the data structure being protected. The threads are the things accessing the data structure. The locks are on the data structure object in order to keep the threads from accessing the data structure in an unsafe way.

Any object can be used as an intrinsic lock (meaning used in conjunction with synchronized). This way you can guard access to any object by adding the synchronized modifier to the methods that access the shared data.

The wait and notify methods are called on objects that are being used as locks. The lock is a shared communication point:

  • When a thread that has a lock calls notifyAll on it, the other threads waiting on that same lock get notified. When a thread that has a lock calls notify on it, one of the threads waiting on that same lock gets notified.

  • When a thread that has a lock calls wait on it, the thread releases the lock and goes dormant until either a) it receives a notification, or b) it just wakes up arbitrarily (the "spurious wakeup"); the waiting thread remains stuck in the call to wait until it wakes up due to one of these 2 reasons, then the thread has to re-acquire the lock before it can exit the wait method.

See the Oracle tutorial on guarded blocks, the Drop class is the shared data structure, threads using the Producer and Consumer runnables are accessing it. Locking on the Drop object controls how the threads access the Drop object's data.

Threads get used as locks in the JVM implementation, application developers are advised to avoid using threads as locks. For instance, the documentation for Thread.join says:

> This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

Java 5 introduced explicit locks implementing java.util.concurrent.locks.Lock. These are more flexible than the implicit locks; there are methods analogous to wait and notify (await and signal), but they are on the Condition, not on the lock. Having multiple conditions makes it possible to target only those threads waiting for a particular type of notification.

Solution 2 - Java

You can use wait() and notify() to synchronize your logic. As an example

synchronized (lock) {
    lock.wait(); // Will block until lock.notify() is called on another thread.
}

// Somewhere else...
...
synchronized (lock) {
    lock.notify(); // Will wake up lock.wait()
}

with lock being the class member Object lock = new Object();

Solution 3 - Java

Think using a real life example, a washroom. When you want to use the washroom at your office, you have two options to make sure no one else will come to the washroom once you are using it.

  1. Lock the washroom door, so everyone else will know that it's used by someone else when they try to open the door
  2. Go to each person in the office, lock them to their chairs (or table, or whatever), go to washroom.

Which option would you take?

Yes, it's the same in the Javaland!.

So in the above story,

  • Washroom = Object you want to lock (that only you need to use)
  • Your staff colleagues = other threads that you want to keep out

So just like in real life, when you have some private business, you lock that object. And when you are done with that object, you let go of the lock!.

(Yes yes!, this is a very simple description on what happens. Of course the real concept is slightly different from this, but this is a starting point)

Solution 4 - Java

You can stop your thread for time as you want using static Thread class method sleep().

public class Main {
    //some code here
    
    //Thre thread will sleep for 5sec.
    Thread.sleep(5000);   
}

If you want to stop some objects you need to call this method's within syncronized blocks.

public class Main {

//some code

public void waitObject(Object object) throws InterruptedException {
    synchronized(object) {
        object.wait();
    }
}

public void notifyObject(Object object) throws InterruptedException {
    synchronized(object) {
        object.notify();
    }
}

}

P.S. I'm sory if I wrong understand your question (English is not my native)

Solution 5 - Java

When you put some code inside synchronized block:

 sychronized(lock){...}

a thread wanting to perform whatever is inside this block first acquires a lock on an object and only one thread at a time can execute the code locked on the same object. Any object can be used as a lock but you should be careful to choose the object relevant to the scope. For example when you have multiple threads adding something to the account and they all have some code responsible for that inside a block like:

sychronized(this){...}

then no synchronization takes place because they all locked on different object. Instead you should use an account object as the lock. Now consider that these threads have also method for withdrawing from an account. In this case a situation may occur where a thread wanting to withdraw something encounters an empty account. It should wait until there's some money and release the lock to other threads to avoid a deadlock. That's what wait and notify methods are for. In this example a thread that encounters an empty account releases the lock and waits for the signal from some thread that makes the deposit:

while(balance < amountToWithdraw){
    lock.wait();
}

When other thread deposits some money, it signals other threads waiting on the same lock. (of course, code responsible for making deposits and withdrawals has to be synchronized on the same lock for this to work and to prevent data corruption).

balance += amountToDeposit;
lock.signallAll;

As you see the methods wait and notify only make sense inside synchronized blocks or methods.

Solution 6 - Java

In Java all Object implements these two methods, obviously if there are not a monitor those two methods are useless.

Solution 7 - Java

Actually, wait, notify member function should not belong to thread, the thing it should belong to name as condition variable which comes from posix thread . And you can have a look how cpp wrap this, it wrap it into a dedicated class std::condition_variable.

Java haven't do this kind encapsulation, instead, it wrap condition variable in more high level way: monitor (put the functionality into Object class directly).

If you don't know monitor or condition variable, well, that indeed make people confused at the beginning.

Solution 8 - Java

  1. Wait and notify is not just normal methods or synchronization utility, more than that they are communication mechanism between two threads in Java. And Object class is correct place to make them available for every object if this mechanism is not available via any java keyword like synchronized. Remember synchronized and wait notify are two different area and don’t confuse that they are same or related. Synchronized is to provide mutual exclusion and ensuring thread safety of Java class like race condition while wait and notify are communication mechanism between two thread.
  2. Locks are made available on per Object basis, which is another reason wait and notify is declared in Object class rather then Thread class.
  3. In Java in order to enter critical section of code, Threads needs lock and they wait for lock, they don't know which threads holds lock instead they just know the lock is hold by some thread and they should wait for lock instead of knowing which thread is inside the synchronized block and asking them to release lock. this analogy fits with wait and notify being on object class rather than thread in Java.

Analogy : a Java thread is a user and the toilet is a block of code which the thread wishes to execute. Java provides a way to lock the code for a thread which is currently executing it using the synchorinized keywokd, and making other threads that wish to use it wait until the first thread is finished. These other threads are placed in the waiting state. Java is NOT AS FAIR as the service station because there is no queue for waiting threads. Any one of the waiting threads may get the monitor next, regardless of the order they asked for it. The only guarantee is that all threads will get to use the monitored code sooner or later.

Source

If you look at the following producer and consumer code:
sharedQueue Object acts inter-thread communication between producer and consumer threads.

import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ProducerConsumerSolution {

	public static void main(String args[]) {
		Vector<Integer> sharedQueue = new Vector<Integer>();
		int size = 4;
		Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer");
		Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer");
		prodThread.start();
		consThread.start();
	}
}

class Producer implements Runnable {

	private final Vector<Integer> sharedQueue;
	private final int SIZE;

	public Producer(Vector<Integer> sharedQueue, int size) {
		this.sharedQueue = sharedQueue;
		this.SIZE = size;
	}

	@Override
	public void run() {
		for (int i = 0; i < 7; i++) {
			System.out.println("Produced: " + i);
			try {
				produce(i);
			} catch (InterruptedException ex) {
				Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
			}

		}
	}

	private void produce(int i) throws InterruptedException {

		// wait if queue is full
		while (sharedQueue.size() == SIZE) {
			synchronized (sharedQueue) {
				System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: "
						+ sharedQueue.size());

				sharedQueue.wait();
			}
		}

		// producing element and notify consumers
		synchronized (sharedQueue) {
			sharedQueue.add(i);
			sharedQueue.notifyAll();
		}
	}
}

class Consumer implements Runnable {

	private final Vector<Integer> sharedQueue;
	private final int SIZE;

	public Consumer(Vector<Integer> sharedQueue, int size) {
		this.sharedQueue = sharedQueue;
		this.SIZE = size;
	}

	@Override
	public void run() {
		while (true) {
			try {
				System.out.println("Consumed: " + consume());
				Thread.sleep(50);
			} catch (InterruptedException ex) {
				Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
			}

		}
	}

	private int consume() throws InterruptedException {
        //wait if queue is empty
        while (sharedQueue.isEmpty()) {
            synchronized (sharedQueue) {
                System.out.println("Queue is empty " + Thread.currentThread().getName()
                                    + " is waiting , size: " + sharedQueue.size());

                sharedQueue.wait();
            }
        }

        //Otherwise consume element and notify waiting producer
        synchronized (sharedQueue) {
            sharedQueue.notifyAll();
            return (Integer) sharedQueue.remove(0);
        }
    }
}

Source

Solution 9 - Java

"This method should only be called by a thread that is the owner of this object's monitor." So I think you must make sure there is a thread who is the monitor on the object.

Solution 10 - Java

Object class is the correct place to make a lock available for every object. Suppose there is a joint bank account and hence multiple users can use the same account for transactions through multiple channels. Currently, the account has a balance of 1500/- and the minimum amount balance to remain in the account is 1000/-. Now, the first user is trying to withdraw an amount of 500/- through ATM and another user is trying to purchase any goods worth 500/- through a swipe machine. Here whichever channel first access the account to perform the transaction acquires the lock on the account at first and the other channel will wait until the transaction is completed and the lock on the account is released as there is no way to know which channel has already acquired the lock and which channel is waiting to acquire a lock. Hence lock is always applied on the account itself rather than a channel. Here, we can treat the account as an object and the channel as a thread.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexander MillsView Question on Stackoverflow
Solution 1 - Javaпутин некультурная свиньяView Answer on Stackoverflow
Solution 2 - JavaAlexView Answer on Stackoverflow
Solution 3 - JavaIshan Thilina SomasiriView Answer on Stackoverflow
Solution 4 - JavaPetr ShypilaView Answer on Stackoverflow
Solution 5 - Javaluke657View Answer on Stackoverflow
Solution 6 - JavaNicolas HENAUXView Answer on Stackoverflow
Solution 7 - JavajeanView Answer on Stackoverflow
Solution 8 - JavaPremrajView Answer on Stackoverflow
Solution 9 - JavaPapa LimaView Answer on Stackoverflow
Solution 10 - Javaarpitbhatt027View Answer on Stackoverflow