2

I have a thread that does display updates on my JFrame by using SwingUtilities.invokeLater. The thread's speed is adjustable, and when it set to full speed (no sleeping between updates), my program gets slowed down badly. I guess the problem is my thread is generating too much SwingUtilities.invokeLater events that JFrame's thread can not consume. So is there anything I can do on my thread to remove previously added but not consumed events? Or should I use some other ways to update JFrame without using SwingUtilities.invokeLater?

Thanks in advance.

user1387622
  • 187
  • 2
  • 9

3 Answers3

4

This might be a perfect job for SwingWorker. You can publish incremental updates, and SwingWorker will batch them to solve the performance problem:

Because the process method is invoked asynchronously on the Event Dispatch Thread multiple invocations to the publish method might occur before the process method is executed. For performance purposes all these invocations are coalesced into one invocation with concatenated arguments.

The code you want to run on the EDT, you add by implementing process(). The list of updates is passed to you in the argument.

Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • +1 for `SwingWorker`. It's the right choice for pacing view updates _and_ synchronizing access to shared data. – trashgod Oct 29 '12 at 17:24
3

It sounds like you want to avoid saturating the event dispatch thread. The class javax.swing.Timer, discussed in How to Use Swing Timers, includes setCoalesce(), which "coalesces multiple pending ActionEvent firings." It may an alternative way to pace your updates.

As mentioned here, SwingWorker is limited to 33 Hz.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • That's a really interesting find. I share your surprise on the post you reference that SwingWorker is backed by a Timer, rather than each EDT event simply scheduling the next when it's executed. And as much surprise that the frame rate is so low, and that it's not configurable. – Mark Peters Oct 29 '12 at 13:25
  • @MarkPeters: It's probably a reasonable compromise to avoid saturating the EDT. – trashgod Oct 29 '12 at 17:28
1

Can you use a simple repaint()? The advantage of that is that multiple calls are merged into one.

(Elaboration added)

Let's say you are constantly updating your GPS location and displaying it in two text fields. Your thread to do the updating:

run() {
  while (keepGoing) {
    Point myLocation = computeMyLocation();
    locationModel.setLocation(myLocation);
    locationComponent.repaint();
  }
}

then, in MyLocationComponent

@Override
public void paintComponent(Graphics g) {
   Point myLocation = locationModel.getLocation();

   // you'd really want a NumberFormat
   latitudeTextArea.setText(String.valueOf(myLocation.y));
   longitudeTextArea.setText(String.valueOf(myLocation.x));

   super.paintComponent(g);
}

The advantage is that this splits the model from the view (if you think of the thread as the controller, this is MVC), and all the threading should work - no need for any invokeLater(). One disadvantage is that your thread needs to know all of the JComponents that need to be updated. In a "real" scenario you'd probably fire events to listeners that trigger the repaints, either from the "controller" (your thread) or from the model.

NOTE: As pointed out by @trashgod, in LocationModel, the getLocation() and setLocation() methods should be synchronized so that updates appear immediately.

user949300
  • 15,364
  • 7
  • 35
  • 66
  • 1
    This is OK for a single thread of execution, but the question specified a separate thread; access to any shared data must be synchronized. – trashgod Oct 29 '12 at 17:33
  • True. LocationModel.getLocation() and setLocation(), which were not given in the example, should be synchronized. I'll add a note. – user949300 Oct 29 '12 at 20:35