Andres is a DZone Zone Leader and has posted 143 posts at DZone. You can read more from them at their website. View Full User Profile

JavaFx: the hidden threading rule

08.13.2008
| 25521 views |
  • submit to reddit
The following post is the result of my latest JavaFx Script experiments mixed with Stu's Java.Next series (1,2). Alright, so Stu decides to showcase how the Java.next languages interop with Java the language, and to do so he chose a Swing based example. Though he did a great job covering each transformation from the original Java example to each of the Java.next candidates, the original example has a slight flaw: it doesn't honor Sun's guidelines for building Swing apps, mainly when creating components and/or changing a component's UI state you must do it inside the Event Dispatch Thread.

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 output

action 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.

 

References
Published at DZone with permission of its author, Andres Almiray. (source)

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

Communicating to developers the possible pitfalls of multi-threadedJavaFxScript applications should be a top priority, don't you think?

*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  

if (!EventQueue.isDispatchThread()) new IllegalStateException().printStackTrace();
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 Boudreau

There have been requests to add event-dispatch thread assertions to AWT and Swing for years.  There is no one better positioned to do this correctly once and for all than the AWT and Swing teams.  This should be part of the JRE, not ad hoc hacked in by all of us for lack of redress in the JRE.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.