What is the correct way to run code in EDT only once?

I have a Swing application that uses Java Thread for continuous work. The results of this operation update the contents of the graph in the user interface:

class ExampleThread { ... public void run() { while (running) { // extract some information ... // show results in UI SwingUtilities.invokeLater(new Runnable() { public void run() { // use information to update a graph } }); // sleep some seconds ... } } ... } 

The problem I am facing is that the EDT is inflated in other operations. In this case, ExampleThread can register various Runnable instances that update the schedule. For my application, this will be a waste of time, as the schedule will be updated several times before showing the results. I would like to run the //update a graph code no more than once during an EDT cycle.

My question is: what is the correct way to ensure that Runnable only starts once?

+4
source share
3 answers

The atomic dirty flag should do the trick. This has the added benefit of adding EDT to the queue if necessary. The big assumption is that your data structure containing the extracted information is thread safe, but this must be true for your code to work correctly above. If this is not the case, consider the SwingWorker approach cited in another answer; You can also combine with a dirty flag to prevent redundant updates.

 AtomicBoolean dirty = new AtomicBoolean(false); while (running) { // extract some information ... if (!dirty.getAndSet(true)) { SwingUtilities.invokeLater(new Runnable() { public void run() { if (dirty.getAndSet(false)) { // use information to update a graph } } }); } // sleep some seconds ... } 
+5
source

I suggest using SwingWorker and using the publish method to publish updates to EDT. Then the EDT will apply one or more updates when the next one is planned. For example, suppose your result is calculating Integer instances:

 // Boolean flag to determine whether background thread should continue to run. AtomicBoolean running = new AtomicBoolean(true); new SwingWorker<Void, Integer>() { public Void doInBackground() { while (running.get()) { // Do calculation Integer result = doCalculation(); publish(result); } } protected void process(List<Integer> chunks) { // Update UI on EDT with integer results. } } 

An alternative approach is to create a single Runnable instance that simply consumes Queue results every time it is scheduled. This is similar to how SwingWorker works under cover art, but gives you a little more control in that you can control the implementation of Queue and avoid constantly saving one of the empty SwingWorker threads.

 private final Queue<Integer> resultQueue = Collections.synchronizedList(new LinkedList<Integer>()); // Thread-safe queue to hold results. // Our re-usable Runnable to be run on the EDT. Consumes all results from the result queue // before releasing the lock, although could obviously change this to consume up to N results. Runnable consumer = new Runnable() { public void run() { synchronized(resultQueue) { Integer result; while ((result = resultQueue.take()) != null) { // Update UI. } } } } ... // Business logic to run on background thread. Conditionally schedules the EDT to run as required. while (true) { Integer result = doCalculation(); synchronized(resultQueue) { boolean scheduleEdt = resultQueue.isEmpty(); // Queue was empty before this result is added so need to schedule the EDT to run again. resultQueue.add(result); } if (scheduleEdt) { SwingUtilities.invokeLater(consumer); } else { System.err.println("EDT already scheduled -> Avoiding unecessary reschedule."); } } 
+3
source

Alternatively, you may need to study javax.swing.Timer .

0
source

All Articles