JavaFx: the hidden threading rule
You can't really blame Stu for it, he is surely more of server/web developer than a desktop one so he probably wasn't aware of the guidelines, he took the code from a JRuby example, which most likely was taken from somewhere else. The real problem is twofold
- bad programming examples since the early days of Swing.
- no way to enforce Sun's Swing guidelines at the compiler level (some tools may provide stricter checks)
Because the amount of bad examples (by bad I mean those that do not follow the guidelines) is much more than the good ones we keep falling in the same hole over and over. The second reason can be mitigated by external tools as code checkers and IDEs. The following example does follow Sun's guidelines, which goes to prove the high level of ceremony of Java the language
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Swing {
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JFrame frame = new JFrame("Hello Swing");
JButton button = new JButton("Click Me");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
JOptionPane.showMessageDialog(null,
String.format("<html>Hello from <b>Java</b><br/>" +
"Button %s pressed", event.getActionCommand()));
}
});
frame.getContentPane().add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
} );
}
}
In Java Swing you must always have to take extreme precautions about threading, one bad move and your app will appear to be unresponsive when doing a long-term computation, scaring the user away. Regular Swing applications will run from the main thread, thus leaving you the task to properly wrap your code with SwingUtilities. What does this have to do with JavaFx Script? there it happens the complete opposite. Every single JavaFx application runs inside the Event Dispatch Thread, always, period. This means you no longer have to worry about paint/repaint issues when changing UI state _but_ you still need to take care of long computations as they will be run inside the EDT. The following fx script demonstrates this fact
import java.lang.System;
import javax.swing.SwingUtilities;
import java.lang.Thread;
class MyThread extends Thread {
override function run():Void {
System.out.println("inside MyThread");
System.out.println( SwingUtilities.isEventDispatchThread() );
}
}
System.out.println( SwingUtilities.isEventDispatchThread() );
var t = new MyThread();
t.start();
// --- output
// true
// inside MyThread
// false
Once you jump out of the EDT you will eventually need to go back to
update UI state. There is no current shortcut alternative for JavaFx
(the jfx team is still working on it), so perhaps we shouldn't put away
SwingWorker just yet. In case you are wondering you can't extend
SwingWorker from JavaFx Script (at least not in a way I could find)
given that it doesn't support generics declarations (yet). The
alternative right now is pretty simple, drop down to the Java level,
subclass SwingWorker and use it from your JavaFx scripts.
Now
I'd like to show the Groovy alternative with a modified example,
instead of displaying a dialog when the button is clicked, its text
will change color from black to red and back
import groovy.swing.SwingBuilder
import javax.swing.SwingUtilities
import static javax.swing.JFrame.EXIT_ON_CLOSE
import java.awt.Color
int count = 0
SwingBuilder.build {
println "building ui inside EDT? ${SwingUtilities.isEventDispatchThread()}"
frame( title: "Hello Swing", pack: true, show: true,
defaultCloseOperation: EXIT_ON_CLOSE ) {
button( "Click Me", actionPerformed: { evt ->
println "action handler inside EDT? ${SwingUtilities.isEventDispatchThread()}"
doOutside {
println "long computation inside EDT? ${SwingUtilities.isEventDispatchThread()}"
doLater {
println "UI state change inside EDT? ${SwingUtilities.isEventDispatchThread()}"
evt.source.foreground = count++ % 2 ? Color.BLACK : Color.RED
}
}
})
}
}
Once you run the example you'll get the following output
building ui inside EDT? true
Which means that SwingBuilder took care of properly building the UI inside the EDT, the static
build() method runs on the EDT. Clicking on the button once yields the following outputaction handler inside EDT? true
long computation inside EDT? false
UI state change inside EDT? true
Two methods should catch your attention:
doOutside and doLater.
Those are handy threading abstractions provided by SwingBuilder. The
first one spawns a new thread and runs the closure on it, the second
one makes sure the closure is run inside the EDT. Simple, easy, Groovy.
But if these threading facilities are not enough you can always
subclass SwingWorker (in Groovy as it supports generics and other Java5
features) and plug it in.The road to JavaFx Script 1.0 is still ahead of us, the JavaFx platform continues to be under construction, expect some updates on threading, hopefully by the time 1.0 is released.
</rant>
Note: I do not posses any inside information on JavaFx, all this material is the result of my own explorations and blog posts around the web.
- Login or register to post comments
- 7430 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
Jim Weaver replied on Wed, 2008/08/13 - 6:30pm
Hi Andres,
Regarding current capabilities and future plans for threading in JavaFX, the Preview SDK has the following class designed to provide an abstraction (given that JavaFX needs to run on devices other than just desktop). See the screenshot below of the JavaFXdocs for the javafx.lang.DeferredTask class. Also, there is a package named javafx.async destined for JavaFX SDK 1.0 that contains classes to abstract asynchronous operations. See the javafx.async.AbstractAsyncOperation JavaFXdocs for example.
Regards,
Jim Weaver
JavaFXpert.com
Andres Almiray replied on Wed, 2008/08/13 - 6:42pm
Hi Jim,
Yes, given that JavaFx targets more platforms basing DeferredTaks in SwingWorker is not a good option. Still what I find a bit strange is that threading in JavaFx Script has been given little attention (blog posts, articles, examples). Swing had (has?) a lot of bad press because of this very issue, pushing a new way to create desktop/mobile apps without addressing threading right away is not (IMHO) a sound choice. Communicating to developers the possible pitfalls of multi-threadedJavaFxScript applications should be a top priority, don't you think? otherwise we will we thrown again into a never ending spiral of bad examples/bad tech/blame Sun blah blah.
My $0.02
Regards,
Andres
PD: I didn't notice DeferredTask 9thanks for the tip) but found AsyncJsonCall. After some googling and inspecting svn reports I found out it uses java.util.concurrent.Future (from the Java side) to do its thing, which is good.
Jim Weaver replied on Wed, 2008/08/13 - 6:59pm
*Excellent* point, Andres! I am making a note to do an article on that topic.
Thanks, and regards,
Jim Weaver
JavaFXpert.com
Andres Almiray replied on Wed, 2008/08/13 - 7:05pm
No problem. Looking forward to the finished MP3 player! :-)
Regards,
Andres
Tim Boudreau replied on Wed, 2008/08/13 - 11:48pm
Agreed, JavaFX's threading model needs to be communicated - and hopefully handled more strictly than Swing's.
One handy trick I sometimes do with the JDK sources is just to compile a JAR with a copy of java.awt.Component, add
to the constructor (I suppose AOP would be more elegant, but this works), and run NetBeans or whatever I'm working on to see if there's any threading evil happening.Jess Holle replied on Fri, 2008/08/15 - 12:43pm
in response to: tim