views:

584

answers:

7

I have an object which does some computation, then for each iteraction I want to draw what happens. While the drawing is happening, I want it to wait.

This is what I did, basically:

synchronized public void compute()
{
    other.mark(variable);
    try
    {
        wait();
    }
    catch(InterruptedException e)
    {
    }
}

in the OtherClass, I have

synchronized public void mark(int var)
{
    //change some stuff
    repaint();
    notify();
}

What happens is that compute() waits forever. I thought this was going to work, since no erros were given by the compiler. Neither class implements Runnable or extends Thread, so maybe that's the problem? I'm not sure since I figure I'd be warned if these objects couldn't use such methods.

I'm thinking it might be an error regarding the logic of the program itself, but in a nutshell that's what I have.

A: 

Object.wait() and Object.notify() are only for use by threads. In the code you show, the method mark(int var) will not return until it is finished, there is no need to wait. Also, synchronized methods are only needed in a multi-threaded program.

Your code should be:

public void compute()
{
    other.mark(variable);
}

public void mark(int var)
{
    //change some stuff
    repaint();
}
Skip Head
This isn't true. You don't have to implement Thread to use wait and notify. And since this is a GUI program, it is (almost) necessarily multi-threaded.
Eddie
I didn't say you need to implement Thread, I meant you need to have threads in use for wait() and notify() to be useful. (Also, you can't implement Thread, you can extend Thread or implement Runnable.
Skip Head
+1  A: 

This simply does not work as you think it does. From Javadoc of wait() method (emphasis mine):

Causes current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.

There is obviously no other thread in your program to wake up the sleeping compute() method.

To solve your particular problem, you either have to have two threads, or, alternatively, implement compute() method as resumable, something like this in pseudo Java:

ComputeStatus status = new ComputeStatus();
do {
    compute(status);  // compute iteration
    mark(status);     // draw iteration
    status.next();    // next iteration
} while (!status.isFinished());

Here ComputeStatus hold the current state of computation, and comupte() knows how to continue the calculation from that state. Whether you change the status in compute() or in main loop, is up to you and the problem you're solving.

javashlook
A: 

Since your program is a GUI program (I'm gathering by the repaint() call), it is inherently multi-threaded, even if you don't know it. (If not, it will behave very badly.)

If you are not using Threads, then you cannot use wait/notify or any other kind of synchronization, since there aren't two threads to synchronize. But you don't have to explicitly use Threads, necessarily, in a GUI program to end up using Threads.

Note that the Java compiler won't warn you if you use methods relying on Threads but don't actually use Threads in any way.

You have one of the following problems:

1) You are using Threads without knowing about it and you are using two different monitor objects. Thus, when you call notify(), it is notifying the monitor for that object, but not for the first object where you are calling wait(). There are many possible solutions. One of the easiest is to use the JDK 5 concurrency utilities to do this, which are MUCH nicer than the built-in base monitor wait/notify methods. Or,

2) You are running in a single Thread and the wait/notify does not good. It just doesn't make sense in a single-Threaded program to wait for another Thread to notify -- there's no other thread that can do so.

Assuming that you are actually using more than one Thread, a good way to solve this with Java 5 and later is with a semaphore, perhaps by, in the class containing mark() and simplifying a bit:

private final Semaphore sem = new Semaphore(1);

public void mark(int var) {
  sem.acquire();
  //change some stuff
  repaint();
  sem.release();
}

waitForSemaphore() {
  sem.acquire();
}

and then in compute, call waitForSemaphore() when you want to wait to be notified by mark(). Because mark() already acquired the semaphore, you'll have to wait for mark() to release the semaphore before compute will be able to get it by calling waitForSemaphore().

Eddie
Could you give me an example of a concurrency utility I could use? At least a name.
Code sample added
Eddie
If I waitForSemaphore then it waits eternally, both before and after using mark. If I don't waitForSemaphore, I can draw as if I wasn't using this.I appreciate the help though. I also have to wait a bit after every drawing (a couple ms), so I don't think I could do it with this.
Are you truly trying to synchronize action between two threads, or are you trying to do some timing? If just timing, then concurrency utilities are the wrong tool for the job.
Eddie
A: 

The repaint method registers the need to paint the component but it doesn't actually paint it, however Java will repaint the object the next chance it gets. If you are trying to make something like an animation then there is no purpose to wait for repaint to complete. Instead I recommend you use a timer. Now you have 2 options for timers. If you are updating something where the timing doesn't have to be exact then often javax.swing.Timer is what you are looking for. You use it like this:

//imports (before class definition)
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

//put this code where you want to start doing calculations
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
    public void actionPerformed(ActionEvent evt) {
        //update your calculations
        model.calculate();
        //tell Java to call paint at the next chance it gets
        viewer.repaint();
    }
};
new Timer(delay, taskPerformer).start();

In the above code the model is the object you want to perform the calculations on and the viewer is the object that paints based on the model.

The swing timer is not very exact in its timing which is fine for lots of things but sometimes you need code to be scheduled more exactly. In that case you may want to use the java.util.Timer class. You use it like this:

//imports (before class definition)
import java.util.Timer;
import java.util.TimerTask;

//inner class that does the calculations
public class CalculateTask extends TimerTask {
    public void run() {
        model.calculate();
        view.repaint();
    }
}

//put this code where you want to start doing calculations
int delay = 0;//time before running CalculateTask.run()
int repeat = 1000; //time between each subsequent rums of CalculateTask.run()
boolean isDaemon = true;//allow java to shutdown without stopping timer
Timer timer = new Timer(isDaemon);
timer.scheduleAtFixedRate(new CalculateTask(), delay, repeat);
James
+1  A: 

Your question suggests that you want to perform some operation that updates the GUI state either when it is finished or notifies the GUI of its progress. This is what SwingWorker was designed for. There are some examples on the linked javadoc for both cases.

Mark
A: 

The wait() never gets released because you are not synchronizing on the same object. Your compute method is in a different object and therefore the notify call is not sharing the same monitor as your mark() method.

The wait/notify mechanism are for shared monitors, that is, they have to be sharing the same thread locking mechanism.

The only way the wait() will 'wake up' is if another thread calls notify() from within a synchronization block on the same object.

Robin
A: 

Unfortunately wait() will never stop waiting. Main reason, look were you put your notify(). It is being called by the same thread, and can't wake itself up.

It's quite simple. mark(int var) will already have finished running by the time you get to the wait() command, so there is no way that the notify() in mark(int var) can wake it.