I would really appreciate your feedback if my current understanding of threading in Swing is correct or hints where I go wrong. I wrote a really basic MVC app that is using a JFrame to display the GUI:
The controller gets called to handle all interactions with the gui and initially creates the window (JFrame is passed by the GUI class). So far there should be the main application thread and the Swing / EDT that is used to display the GUI:
Going from here action handlers in the view call methods in the controller. These handlers run on the EDT. So when the handler calls a controller method:
The actions in the controller method are still carried out on the EDT and should be either quick or wrapped in a Swing Worker to keep the GUI responsive. So far I struggle to
have an idea what would be fast enough. Or is it good practice to use Swing Workers for all of these calls coming from EDT event handlers?
Finally, the other way around I understood that all manipulations that call Swing methods should be carried with invokeLater/wait if quick or SwingWorkers if not. In my app all Swing objects are contained in the view
class (except for the initial window creation). The controller calls view methods which manipulate JLabels, Sliders etc.. Is it necessary to wrap all these Swing component handling in the view with InvokeLater and SwingWorker constructs? And
again how to determine what is too lengthy for invokeLater?
You know that Java8+ allows you to abbreviate that anonymous inner class to a λ? You can replace lines 7‑13 with
SwingUtilities.invokeLater(() -> controller.showDesktopAppView());
Similarly for an Action Listener anonymous class:-
blueButton.addActionListener(e -> greenButton.setBackground(Color.RED));
There is something a bit unusual about that, creating the controller outwith the EDT.
Then you get to a long task, which you are going to perform with a SwingWorker object, presumably. The tutorials link should tell you how to use a swing worker object to create a separate Thread. I don't know how slow you would regard as too slow. Are you doing other things, e.g. animation, on the EDT? If so, you will notice every delay. If your animation is running at intervals of 33ms (about 30 times per second), you will probably notice jerkiness to your animation if the swing worker takes > 25ms to complete a task on the EDT. If you are not noticing any jerkiness or slow responses when you run your app on a slow computer, then assume everything is working fast enough. It isn't really possible to be any more specific than that. If you are downloading something from a website, you need to allow a few seconds even for the quickest query, so you will want a swing worker and a separate thread for that sort of thing. A 2″ delay between your clicking the button and the other button turning red will produce complaints about slow execution from all your users.
On the EDT started with invokeLater? Yes. Using SwingWorker or repeated invokeLater calls? No. I can see no point in using a SwingWorker for something like
The controller calls view methods which manipulate JLabels, Sliders etc.. Is it necessary to wrap all these Swing component handling in the view with InvokeLater and SwingWorker constructs?
resultsTextField.setText("CodeRanch is brilliant");
which anyway will run much faster than the repainting of that component to show the change. A repeated invokeLater call will simply plug you into the EDT which should already be running, so you usually don't need that either. You can use invokeAndWait(), but once you have tried it and seen how many checked exceptions you have to handle, you will always use invokeLater in future
I hope this has answered your question; sorry if I have answered something completely different.
So starting again at the beginning:
So here we have the "main thread" and the "Swing Thread" aka EDT. Since view and controller are wrapped in an invoke later construct they are created in the EDT and
anything that happens inside and between those to stays on the EDT. Unless it calls on components that run in a different thread or includes creation of a new thread like swing worker (even though that
returns to the EDT). Listener action happens on the EDT.
So anything in there that is to lengthy for the GUI needs a SwingWorker. Otherwise no invokeLarters are necessary since it is happening in the EDT anyway. When the view calls on the playlist/model that is
started from the EDT since that's were the controller acts. Model actions initiated by the controller then happen on the main thread and may move on to other threads, but returns to the EDT when the call returns to
the controller. So this won't require any inivokeLater actions to bring back to the EDT. Slow going model stuff also doesn't affect the EDT since it happens elsewhere. Only if the action in the controller prior or past the model action
would be lenghty that calls for a SwingWorker. But seems wrong anyway since the controller should not work hard and only coordinate things, right?
I hope that's right so far and I did not mess up completely now. The only thing I am unclear about now is listening. The controller is a listener of the model. Which reside in different threads. Therefore, I assume that either
the model notifyListeners() call should be wrapped with an invoke later (if all listeners are EDT residents) or maybe better the listener should start its reactToNotification() method to sync ingoing listener events with the EDT?
Hope that clarifies things a little bit?
inform the controller about changes in the model e.g. the playlist to update the view accordingly.
remark the polite way for telling me it is utterly wrong or is that just me being paranoid, because I spend to much time with the English?
I'm not sure about all that
Which thread it runs on depends on which thread it was called from.
So, in your example, even though playlist is instantiated in the Main thread, if it is accessed (via a call to the controller in a listener for example) in the EDT it will run in the EDT.
That code runs on the EDT.
If skipBwd happened (for whatever reason) to call something on playlist then that would also run on the EDT.
The only way for it to not run on the EDT is for it to be launched in another thread, eg via a SwingWorker.
Since all calls in the application are either initiated by user GUI interactions or model changes, which all result in listener actions the only thing to take care of is to use Swing Workers in case of long running processes.
Plus using Invoke later to set up the GUI initially. I will still have a good look at the Oracle general threading tutorial. Shouldn't do me any harm.
mark q. murphy wrote:Since all calls in the application are either initiated by user GUI interactions or model changes, which all result in listener actions the only thing to take care of is to use Swing Workers in case of long running processes.
Yeah! That's a much clearer description of why and how to use threads in a Swing application.