Swingworker, Thread.Sleep(), or Javax.Swing.Timer? I Need to "Insert a Pause"

java - SwingWorker doens't run when calling Thread.sleep() in EDT thread

My SwingWorker is for counting time, it will update time every 1 second.

If the only responsibility of this worker is to update a label with the elapsed time, then a Swing Timer is a better candidate to do it. Let the SwingWorker doInBackground() implementation do the heavy calculation and finally the done() implementation may just stop the timer and display the final results.

Do not call Thread.sleep() in the EDT because it will freeze the entire GUI event processing and you'll probably see just the final result without the intended periodic updates.

Update

Adding just a simple snippet to start with:

public void startCalculation() {

Timer timer = new Timer(1000, new ActionListener() {

AtomicInteger elapsedSeconds = new AtomicInteger();

@Override
public void actionPerformed(ActionEvent evt) {
label.setText(String.format("Elapsed time: %d seconds", elapsedSeconds.incrementAndGet()));
}
});

SwingWorker<Integer, Void> worker = new SwingWorker<Integer, Void>() {

@Override
protected Integer doInBackground() throws Exception {
// do heavy stuff
Thread.sleep(3000); // it is safe to "sleep" here because it's not executed in the EDT
return 1;
}

@Override
protected void done() {
timer.stop();
// update the GUI with the results here if it's required
}
};

timer.start();
worker.execute();
}

Thread.sleep() pauses JFrame before other statements

UI is not updated instantaneously.

When you call

flipCard(choice1, arg1);
flipCard(choice2, arg2);

I assume flipCard updates the UI in some way. And your expected behaviour is that choice1 and choice2 cards will be flipped, wait 3 seconds, and flip them again. But the actual behaviour you get is that nothing happens until 3 seconds have passed, after 3 seconds, the cards flipped twice.

What you need to understand is that UI has a frame rate. When you call flipCard, the card will not be flipped until the next frame. During the time between this frame and the next, Thread.sleep is called so everything, including the frames, pause for 3 seconds. That's why the UI updated after 3 seconds' pause.

I suggest you to use javax.swing.Timer or javax.swing.SwingWorker. See here or here for more info.

Updating swing components correctly?

There are a number of important concepts you seem to be missing.

  1. Swing is an event driven environment. That means that there is no means (or at least only a very few) that you can "wait" for user input, typically, you just need to react to their interaction.
  2. Swing is driven by a single thread, known as the Event Dispatching Thread (AKA EDT). It is the responsibility of this thread to dispatch/process events coming into the application to the appropriate parts of the application so that they can take action.
  3. The repaint manager posts its update requests onto the EDT.

ANY action you take that stops the EDT from carrying out this work will make your application look like it's hung.

You must NEVER carry out any time consuming operations (such as I/O, loops or Thread#sleep for example) on the EDT, doing so will make your application "pause", which is never pretty.

Have a read through Concurrency in Swing for more information.

Now, you have a number of choices. You could use a Thread to "wait" in the background and turn the cards back or you could use a SwingWorker or a javax.swing.Timer.

The other problem you have, is that you should NEVER update any UI components from any Thread other than the EDT. This means if you were to use a Thread, you would become responsible for re-syncing that thread with the EDT. While not difficult, it just becomes messy.

SwingWorker and javax.swing.Timer have functionality that make this much easier.

Threads and SwingWorker are great for performing background processing and would simply be overkill for this problem. Instead, a javax.swing.Timer would fit perfectly here.

if (!Control.model.twoCardsTurned)
{
if ("unturn".equals(action))
{
new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
keypadArray[pos].setIcon(back);
keypadArray[Control.model.lastCard].setIcon(back);
System.out.println("Card: "+pos+" unset");
System.out.println("Card: "+Control.model.lastCard+" unset");
}
}).start();
}
}

This is a really simple example. You might like to put in some controls that will prevent the user from clicking anything until the timer fires, for example ;)

Timer won't stop when there is a Thread.sleep inside Java

This doesn't begin to make sense.

  • You're scheduling a timer task to run every 1000 milliseconds.
  • The task has 54 internal iterations which each display an image and in all cases except the last sleep for 200ms.
  • Total time so far 10600 milliseconds plus however long it takes to display the images
  • The timer will reschedule the task after 1000ms of that: meanwhile
  • the task will cancel the timer on the last iteration.

So you will get:

  • 53 images and 53 200ms sleeps
  • a restart of the task after about 10% of that is complete
  • a 54th image
  • the timer gets cancelled.

So you get about ten or eleven iterations of the task, mostly in parallel.

I suggest you:

  • schedule the task at 200ms intervals, have it display the next sequential image every time it is invok d, wrapping around to the beginning or cancelling the timer or whatever you want when it gets to the last image, and

  • get rid of the internal loop.



Related Topics



Leave a reply



Submit