The vast majority of JMRI code (and programmers) don't have to worry about threading. Their code gets invoked, and invokes other code, and threading takes care of itself. This is particularly true of event-based code, which responds to events from e.g. the GUI or a layout object changing, and calls methods on other objects, which may in turn create more events.
This simplicity comes from using a single thread for processing most of JMRI's activity: The Java Swing event thread.
Note that this does not mean that other things can't be happening. For example, this script fragment:
state = sensors.provideSensor("mySensor").getState() turnouts.provideTurnout("myTurnout").setState(THROWN) print state == sensors.provideSensor("mySensor").getState()
There are times when you might want to do something a bit more complex using an additional thread:
For example, if you want to read a bunch of data from a file, spend some time munging it, and then create a window to present it all, you might want to do all that work on a separate thread. At the end, when it's time to set your new frame visible, you have to to that on the Swing (GUI) thread. Here's the code to do that:
frame = new JmriJFrame(); // frame declared as instance variable // spend a long time reading data and configuring the frame ThreadingUtil.runOnGUI( ()->{ frame.setVisible(); });
ThreadingUtil separates operations on the GUI (e.g. Java Swing) thread, and operations on the Layout (e.g. Sensors, Turnouts, etc) thread. There's no real difference now, but in the interest of perhaps someday needing to separate those, we've introduced the two versions now. Please try to pick the mostly-likely-right one when coding.
(N.B.: You'll find lots of older cases that use explicitly use javax.swing.SwingUtilities.invokeLater(r) or javax.swing.SwingUtilities.invokeAndWait(r); these should be migrated to the newer JMRI-specific methods as time is available to keep our code just a tiny bit cleaner and more flexible.)
If you do write a method that has to be called on a particular thread, please annotate it with a @InvokeOnGuiThread or @InvokeOnLayoutThread to ease later static checking.
If you have ensured that a method is thread safe and may be run on any thread, please mark it with @InvokeOnAnyThread
If you want to check at runtime that something is running on the intended thread:
ThreadingUtil.requireGuiThread(log);or
ThreadingUtil.requireLayoutThread(log);
That check does take a bit of time, so it should probably only be done on entry to a large
chunk of code, or perhaps be protected by a check of log.isDebugEnabled()
. If the call
occurs on the wrong thread, a Log4JUtil.warnOnce
logs the first occurance with
traceback info. In addition to the @InvokeOnGuiThread
,
@InvokeOnLayoutThread
and @InvokeOnAnyThread
annotations mentioned here,
there are @ThreadSafe
, @NotThreadSafe
, @Immutable
, and
@GuardedBy
annotations that are useful. See the discussion on the JMRI SpotBugs page.
If you do need to launch a thread of your own, there are a few things to remember:
ThreadingUtil.getJmriThreadGroup()
as the first argument to the Thread
constructor. This makes it easier to sort JMRI-created threads from system and library
threads when debugging.
Bottom line recommendation: Never swallow InterruptedException
! If
you call a method that throws it, your choices are (from best to worst):
catch
it, just
have your method says that it throws InterruptedException
.try { wait() } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
For more background, please read this article.
The jmri.util.ThreadingUtil class provides the runOnGUIDelayed and runOnLayoutDelayed methods to make it easy to run some code after a delay. In general, you should use these whenever possible instead of explicit javax.swing.Timer or java.util.Timer objects.
We recommend you not use the java.util.Timer class directly because of significant issues:
The jmri.util.TimerUtil can help with these issues.
Using a BlockingQueue from the java.util.concurrent package can remove the need to mess with thread synchronization and locking. That can be a huge win!
The java/test/jmri/util/ThreadingDemoAndTest.java file is an example of using threads for join, interrupt, etc. It also includes a BlockingQueue example.
The jmri.util.PropertyChangeEventQueue class can handle listening to lots of NamedBeans without possibly missing or overlapping some notifications. The jmri.jmrit.automat.Siglet class is an example of using this.
To do this when using an Oracle JVM (most common case), open a separate window and on that command line enter the command 'jvisualvm'. This will open a new window with tools for browsing all the Java currently running on your machine. Then
If for some reason you can't use jvisualvm, then:
ps | grep java | grep apps | awk '{print $1}'
kill -s QUIT 6190
kill -s QUIT `ps | grep java | grep apps | awk '{print $1}'`
Note that if running under Ant or an IDE, you might get more than one dump: The one you're looking for from JMRI, plus maybe one from Ant or the IDE itself.