4

I have a JPanel class which starts another thread using "implements runnable". This other thread will then at various points call a method in the JPanel class which on doing so will then need to wait for the user input. I have attempted to implement it like this:

Method in JPanel class called by other thread that needs to wait:

public void methodToWait()
{
    while(conditionIsMet)
    {
        try
        {
            wait();
        }
        catch
        {
            e.printStackTrace();
        }
    }
}

Method in JPanel class that notifies on wait on user input:

public void mouseClicked(MouseEvent event)
{
    notifyAll();
}

However, upon running the application it throws a "java.lang.IllegalMonitorStateException" upon calling the wait, why is it doing this and how do I resolve the issue?

xingbin
  • 27,410
  • 9
  • 53
  • 103
Jason
  • 143
  • 1
  • 6
  • If you don't know _why_ Java forces you to lock a mutex before calling `o.wait()`, then you probably could learn something from this tutorial: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html – Solomon Slow May 07 '18 at 17:03

1 Answers1

7

See the doc of wait, notify and notifyAll:

Thorws IllegalMonitorStateException - if the current thread is not the owner of the object's monitor.

That means you can not call them until you have aquired the monitor lock, in other words, until you have entered the synchronized block or synchronized method(check this for more).


Another important thing is, you should synchronize on the same object.

  • When you use synchronized block with an explict object, you should call wait and notify on this object.
  • When you use synchronized method, you are implictly synchronizing on this, so you should call this.wait() and this.notify()(keyword this is not mandory).

In this case, you need create an Object as monitor lock and share it between different classes.


Compliant example:

synchronized (obj) {
    while (<condition does not hold>)
        obj.wait();
    ... // Perform action appropriate to condition
}

synchronized (obj) {
    ... // Prepare the condition
    obj.notifyAll();
}

Noncompliant example:

void waitMethod() {
    wait(); // throws IllegalMonitorStateException  
}

void notifyMethod() {
    notify(); // throws IllegalMonitorStateException  
}

Noncompliant example:

synchronized (obj1) {
    while (<condition does not hold>)
        obj1.wait();
    ... // Perform action appropriate to condition
}

synchronized (obj2) {
    ... // call notifyAll on obj2 will not stop the wait on obj1
    obj2.notifyAll();
}

Noncompliant example:

in class1
synchronized void waitMethod() {
    while(someCondition()) {
        wait(); 
    }
}

in class2
synchronized void notifyMethod() {
    notify(); // call notifyAll on class2 will not stop the wait on class1
}
xingbin
  • 27,410
  • 9
  • 53
  • 103
  • Thanks, the exception has stopped but the notifyAll is not stopping the wait. Is it because the mouseClicked event is in a seperate "class"? In that it's under: this.table.addMouseListener(new MouseAdapter() – Jason May 07 '18 at 14:23
  • @Jason That's right. You need synchronize on the same object. You can create an `Object` for synchonization and share this object in different classes. – xingbin May 07 '18 at 14:24
  • 1
    Thank you so much, don't know why it took me two attempts to understand that as it seems so simple now! – Jason May 07 '18 at 15:00
  • @Jason Glad to help :) – xingbin May 07 '18 at 16:05