As is widely known, anything related to Swing components must be done on the event dispatch thread. This also applies to the models behind the components, such as TableModel. Easy enough in elementary cases, but things become pretty complicated if the model is a "live view" of something that must run on a separate thread because it's changing quickly. For example, a live view of a stock market on a JTable. Stock markets don't usually happen on the EDT.
So, what is the preferable pattern to (de)couple the Swing model that must be on the EDT, and a "real", thread-safe model that must be updateable from anywhere, anytime? One possible solution would be to actually split the model into two separate copies: the "real" model plus its Swing counterpart, which is is a snapshot of the "real" model. They're then (bidirectionally) synchronized on the EDT every now and then. But this feels like bloat. Is this really the only viable approach, or are there any other, or more standard, ways? Helpful libraries? Anything?
You can use invokeAndWait() and invokeLater() to update a Swing component from any arbitrary thread.
An invokeLater() method is a static method of the SwingUtilities class and it can be used to perform a task asynchronously in the AWT Event dispatcher thread. The SwingUtilities. invokeLater() method works like SwingUtilities. invokeAndWait() except that it puts the request on the event queue and returns immediately.
I can recommend the following approach:
Example Code
public class MyStockPanel extends JPanel {
private final BlockingQueue<StockEvent> stockEvents;
// Runnable invoked on event dispatch thread and responsible for applying any
// pending events to the table model.
private final Runnable processEventsRunnable = new Runnable() {
public void run() {
StockEvent evt;
while ((evt = stockEvents.poll() != null) {
// Update table model and fire table event.
// Could optimise here by firing a single table changed event
// when the queue is empty if processing a large #events.
}
}
}
// Called by thread other than event dispatch thread. Adds event to
// "pending" queue ready to be processed.
public void addStockEvent(StockEvent evt) {
stockEvents.add(evt);
// Optimisation 1: Only invoke EDT if the queue was previously empty before
// adding this event. If the size is 0 at this point then the EDT must have
// already been active and removed the event from the queue, and if the size
// is > 0 we know that the EDT must have already been invoked in a previous
// method call but not yet drained the queue (i.e. so no need to invoke it
// again).
if (stockEvents.size() == 1) {
// Optimisation 2: Do not create a new Runnable each time but use a stateless
// inner class to drain the queue and update the table model.
SwingUtilities.invokeLater(processEventsRunnable);
}
}
}
As far as I understand, you don't want to implement Swing model interfaces in your real model, do you? Can you implement a Swing model as a "view" over a part of a real model? It will translate its read-access getValueAt() to the calls of the real model, and the real model will notify Swing model about the changes , either providing a list of changes or assuming that Swing model will take care of quering the new values of everything it currently is showing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With