I'm trying to learn how to pause and resume a thread in java. I'm using an Applet
that implements Runnable
has 2 buttons "Start" and "Stop".
public void init(){
th = new Thread(this);
th.start();
btn_increment = new Button("Start");
btn_increment.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ev){
th.notify();
}
});
add(btn_increment);
btn_decrement = new Button("Stop");
btn_decrement.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ev){
try{
th.wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
});
add(btn_decrement);
}
The run method of the thread:
public void run(){
while(true){
repaint();
try{
Thread.sleep(20);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
Now whenever I try to to pause or resume the thread an exception is thrown:
Exception in thread "AWT-EventQueue-1" java.lang.IllegalMonitorStateException
Notes:
The previous code runs perfectly if I use the deprecated methods suspend()
and resume()
, but the documentation points out at using notify()
and wait()
instead with synchronization. I tried adding the word synchronized
to the actionPerformed
method, but it still throws the exception.
Can someone please explain WHY this isn't working and how to solve the synchronization problem? Few explanation points would really be of much help ;)
You have misunderstood how
wait()
works. Callingwait
on aThread
object does not pause that thread; it instead tells the currently running thread to wait for something else to happen. To explain why, I'll need to back up a bit and explain whatsynchronized
actually does.When you enter a
synchronized
block you obtain the monitor associated with an object. For example,obtains the monitor associated with the object
foo
.Once you have the monitor, no other threads can obtain it until you exit the synchronized block. This is where
wait
andnotify
come in.wait
is a method on the Object class that tells the currently running thread to temporarily release the monitor it holds. This allows other threads to synchronize onfoo
.This thread will not resume until someone else calls
notify
ornotifyAll
onfoo
(or the thread is interrupted). Once that happens, this thread will attempt to re-acquire the monitor forfoo
and then continue. Note that if any other threads are waiting to obtain the monitor then they might get in first - there is no guarantee of the order the JVM will hand out locks. Note thatwait()
will wait forever if no-one callsnotify
ornotifyAll
. It's usually best to use the other form ofwait
that takes a timeout. That version will wake up when someone callsnotify
/notifyAll
or when the timeout has expired.So, you need one thread to do the waiting and a different thread to do the notifying. Both
wait
andnotify
must hold the monitor on the object they are trying to wait on or notify; this is why you are seeing the IllegalMonitorStateException.An example might help you understand:
Your Applet code can then do this:
Note that the Applet class does not care about how the scheduler pauses/resumes and does not have any synchronized blocks.
So a possible sequence of events here is:
Does that all make sense?
Having a separate LOCK variable is not required; I've done that to highlight the fact that you are not calling wait/notify on a
Thread
instance. Similarly, the logic inside the RepaintScheduler is not ideal but is just there to illustrate how wait/notify could be used.I think you have to synchronize on the thread in order to call wait and notify. Try to use
and the same with
wait()
.You can't just call
notify
andwait
. You have to wait for something. And before callingnotify
, you have to make it so that there's nothing to wait for anymore.If your blocks aren't already synchronized, then something is wrong in your design.
How can you call
wait
unless you have something to wait for? And how can you know that there is something to wait for if you haven't checked? And how can you check without synchronizing with the code that controls whether that thing has happened yet or not?How can you call
notify
unless something just happened that you need to notify the thread about? And how could something have happened that another thread cares about if you don't hold the lock that would tell that thread about it?You should use
wait
like this:And that
something_to_wait_for
should check something that is protected by synchronization. And you can't makesomething_to_wait_for
synchronized because then you have a race condition -- what if that something happens aftersomething_to_wait_for
returns but before you enterwait
? Then you are waiting for something that already happened! So you need synchronization fundamentally. If you are just adding it at the end, your design is broken.The solution in your case is probably to add something to wait for. Perhaps a simple boolean variable is all you need. Then your code can be
while (should_wait) wait();
,should_wait = true;
, andshould_wait = false(); notifyAll()
. You'll needsynchronized
to protect the boolean and thewait
/notify
logic.