tags:

views:

82

answers:

3

I need to perform a task repeatedly that affects both GUI-related and non GUI-related objects. One caveat is that no action should performed if the previous task had not completed when the next timer event is fired. My initial thoughts are to use a SwingTimer in conjunction with a javax.swing.SwingWorker object. The general setup would look like this.

class
{
    timer = new Timer(speed, this);
    timer.start(); 

    public void actionPerformed(ActionEvent e) 
    {
        SwingWorker worker = new SwingWorker() {
            @Override
            public ImageIcon[] doInBackground() {
                // potential long running task
            }

            @Override
            public void done() {
                // update GUI on event dispatch thread when complete
            }
    }
}

Some potential issues I see with this approach are:

1) Multiple SwingWorkers will be active if a worker has not completed before the next ActionEvent is fired by the timer.

2) A SwingWorker is only designed to be executed once, so holding a reference to the worker and reusing (is not?) a viable option.

Is there a better way to achieve this?

+1  A: 

Have you looked at using a simple Java Timer, and a ReadWriteLock to determine if a task is running when the timer triggers again ? In this situation you could simply bail out of that particular iteration and wait for the next.

Brian Agnew
+1  A: 

Why do you use a Timer? It would be simpler to keep the 'worker' running all the time, pausing via sleep() whenever the task took too little time to complete. You can still update things in the event dispatch thread using something like the following:

Thread background = new Thread(new Runnable() {
  public void run() {
    while ( ! stopRequested ) {
       long start = System.currentTimeMillis();
       // do task
       long elapsed = start - System.currentTimeMillis();

       SwingUtilities.invokeLater(new Runnable() { 
          public void run() { 
             // update UI
          }
       });
       if (elapsed < tickTime) {
          Thread.sleep(tickTime - elapsed);
       }
    }
  }
}.start();
tucuxi
+1  A: 

For (1), the scheduleAtFixedRate() method on ScheduledThreadPoolExecutor might be useful. From the javadocs:

If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.

For (2), it looks like you could define a subclass of SwingWorker and construct new instances of the subclass for each iteration, instead of instantiating an anonymous subclass.

Kevin K