I was looking for a way to make one thread wait/sleep until another thread signalled that something was ready. The waiting thread should wake up, process the data that was made available, then go back to sleep until the other thread signalled again.
The simplest method I could find was Object.wait() and Object.notify(), which behaved like a semaphore initialised to value 0. However, without the synchronized statements around notify/wait, Java always threw IllegalMonitorStateException when the thread was not the monitor owner. So I simply put them around the code like shown below.
THREAD 1: running infinite loop
public class Main {
private Handler handler; // only one instance (singleton pattern)
public void listen() {
while (true) {
try {
synchronized (handler) {
handler.wait();
int value = handler.getSize();
// do something
}
} catch (InterruptedException e) {
// ...
}
}
}
}
THREAD 2: Some other class calls removeItem
public class Handler {
// SINGLETON PATTERN - ONLY ONE INSTANCE
private ArrayList<Integer> sharedList;
private Handler() {
sharedList = new ArrayList<>();
}
public void addItem(Integer i) {
synchronized (sharedList) {
// add to list
}
}
public void removeItem(int i) {
synchronized (sharedList) {
// remove item
// notify that something is removed
synchronized (this) {
this.notify(); // this == handler
}
}
}
public int getSize() {
synchronized (sharedList) {
return sharedList.size();
}
}
}
It seems to work perfectly fine but not sure if there is a hidden bug.
My question is: Is this safe? Does wait release the instance lock for handler/this so notify can acquire the lock?
waitexplicitly releases the lock so other threads can use the class ("explicitly" means it's in the docs somewhere, I just didn't look it up).Mainuses the lock externally, but all ofHandler's methods encapsulate the lock. That's weird. I would be consistent in my encapsulation and makelisten()a method inHandler.thisis quite common. There's reasons to not do it, but there's probably equal reasons to do it. For example, when you take the lock ofHandlerinMain, there's valid reasons why sometimes you need to be able to do that. You should however document when you synchronize onthisbecause it's now basically part of your object's public API.synchronizedblock. As the documentation says, the check and wait should even happen in a loop, before acting on the condition, still while holding the lock as otherwise, there is no guaranty that the condition still holds. Since it’s hard to believe that your// do somethingdoes something useful with only having thesize, it’s very likely that whatever happens there, is broken.wait()andnotify()have no memory. If some thread A callso.notify()at a moment when no other thread is waiting, thennotifydoes nothing at all. If some other thread B subsequently callso.wait()then it will wait until the next time (if any) that some other thread callso.notify().