Files
JIMRI/help/en/html/doc/Technical/Threads.shtml
T
2026-06-17 14:00:51 +02:00

260 lines
12 KiB
Plaintext

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content="HTML Tidy for HTML5 for Apple macOS version 5.8.0">
<title>JMRI: Threading</title>
<meta name="author" content="Bob Jacobsen">
<meta name="keywords" content="JMRI technical code threading">
<!--#include virtual="/help/en/parts/Style.shtml" -->
</head>
<body>
<!--#include virtual="/help/en/parts/Header.shtml" -->
<div id="mBody">
<!--#include virtual="Sidebar.shtml" -->
<div id="mainContent">
<h1>JMRI Code: Threading</h1>
<p>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.</p>
<p>This simplicity comes from using a single thread for processing most of JMRI's activity:
The Java Swing event thread.</p>
<p>Note that this does <em>not</em> mean that other things can't be happening. For example,
this script fragment:
<div class="wide">
<pre>
state = sensors.provideSensor("mySensor").getState()
turnouts.provideTurnout("myTurnout").setState(THROWN)
print state == sensors.provideSensor("mySensor").getState()
</pre></div>
can print either true or false, because changing that turnout <em>can</em> change associated
sensors instantaneously.
<p>There are times when you might want to do something a bit more complex using an additional
thread:</p>
<ul>
<li>You might want to put long-running processing in a separate thread to keep the rest of
JMRI responsive.</li>
<li>The easiest way to code a state machine that talks to layout hardware might be to use a
separate thread.</li>
<li>You might be interfacing to some other existing code that uses threads.</li>
</ul>
<p>We would prefer that you handle the threading issues that arise in that case via the <a href=
"https://jmri.org/JavaDoc/doc/jmri/util/ThreadingUtil.html">jmri.util.ThreadingUtil</a>
class. ThreadingUtil provides utilities that make it easy to invoke needed functions on the
right thread.</p>
<p>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, <a href=
"http://web.archive.org/web/20090526170426/http://java.sun.com/developer/JDCTechTips/2003/tt1208.html">
you have to do that on the Swing (GUI) thread</a>. Here's the code to do that:</p>
<div class="wide">
<pre>
frame = new JmriJFrame(); // frame declared as instance variable
// spend a long time reading data and configuring the frame
ThreadingUtil.runOnGUI( ()-&gt;{ frame.setVisible(); });
</pre></div>
<p>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.</p>
<p>(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.)</p>
<p>If you do write a method that has to be called on a particular thread, please annotate it
with a <a href=
"https://jmri.org/JavaDoc/doc/jmri/InvokeOnGuiThread.html">@InvokeOnGuiThread</a> or <a href=
"https://jmri.org/JavaDoc/doc/jmri/InvokeOnLayoutThread.html">@InvokeOnLayoutThread</a> to
ease later static checking.</p>
<p>If you have ensured that a method is thread safe and may be run on any thread, please mark
it with <a href=
"https://jmri.org/JavaDoc/doc/jmri/InvokeOnAnyThread.html">@InvokeOnAnyThread</a></p>
<p>If you want to check at runtime that something is running on the intended thread:</p>
<pre>
ThreadingUtil.requireGuiThread(log);
</pre>or
<pre>
ThreadingUtil.requireLayoutThread(log);
</pre>
<p>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 <code>log.isDebugEnabled()</code>. If the call
occurs on the wrong thread, a <code>Log4JUtil.warnOnce</code> logs the first occurrence with
traceback info. In addition to the <code>@InvokeOnGuiThread</code>,
<code>@InvokeOnLayoutThread</code> and <code>@InvokeOnAnyThread</code> annotations mentioned here,
there are <code>@ThreadSafe</code>, <code>@NotThreadSafe</code>, <code>@Immutable</code>, and
<code>@GuardedBy</code> annotations that are useful. See the discussion on the <a href=
"SpotBugs.shtml#annotations">JMRI SpotBugs page</a>.</p>
<p>If you do need to launch a thread of your own, there are a few things to remember:</p>
<ul>
<li>Please put your thread into the JMRI thread group by putting
<code>ThreadingUtil.getJmriThreadGroup()</code> as the first argument to the <a href=
"https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#constructor.summary">Thread
constructor</a>. This makes it easier to sort JMRI-created threads from system and library
threads when debugging.
</li>
<li>Please make sure that your new thread(s) are terminated properly at the end each
<a href="JUnit.shtml">JUnit test</a> for your code. If you leave them running, they
interfere with later tests. See the <a href="#ending">next section</a> for how to terminate
threads.
</li>
</ul>
<h3 id="ending">Ending Threads</h3>
This is really about interrupt() and our use of it.
<p>Bottom line recommendation: <em>Never</em> swallow <code>InterruptedException</code>! If
you call a method that throws it, your choices are (from best to worst):</p>
<ol>
<li>InterruptedException means your operation has been interrupted: Some other piece of
code has <em>deliberately</em> asked your operation to end. This is not an error, and
shouldn't be treated as one. If you can, terminate what the code is doing and return to
normal operation.</li>
<li>If you can't reliably terminate whatever is being done, perhaps because you're code is
a low-level part, just pass the InterruptedException up: Don't <code>catch</code> it, just
have your method says that it <code>throws InterruptedException</code>.</li>
<li>Sometimes you can't change the signature of your method: Perhaps it's overriding a Java
definition by inheritance. Only if you can't terminate cleanly or pass it up, you can mark
the interrupt and continue:
<div class="wide">
<pre>
try {
wait()
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
</pre></div>
This will let execution continue, as if you hadn't received the interrupt, but the <em>
next</em> that execution hits a blocking call, that call will immediately get an
interrupt too. Perhaps it can handle it better!
</li>
</ol>
<p>For more background, please read <a href=
"https://www.ibm.com/developerworks/library/j-jtp05236/index.html">this article</a>.</p>
<h3 id="timer">Timed Events</h3>
<p>The <a href=
"https://jmri.org/JavaDoc/doc/jmri/util/ThreadingUtil.html">jmri.util.ThreadingUtil</a> class
provides the <a href=
"https://jmri.org/JavaDoc/doc/jmri/util/ThreadingUtil.html#runOnGUIDelayed-jmri.util.ThreadingUtil.ThreadAction-int-">
runOnGUIDelayed</a> and <a href=
"https://jmri.org/JavaDoc/doc/jmri/util/ThreadingUtil.html#runOnLayoutDelayed-jmri.util.ThreadingUtil.ThreadAction-int-">
runOnLayoutDelayed</a> 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.</p>
<p>We recommend you not use the java.util.Timer class directly because of significant
issues:</p>
<ul>
<li>Each Timer object creates and keeps its own thread, which is very hard to end. This can
cause significant memory use if not handled carefully.</li>
<li>The requested operation is run on the Timer's own thread, which isn't the layout or GUI
thread.</li>
</ul>
<p>The <a href="https://jmri.org/JavaDoc/doc/jmri/util/TimerUtil.html">jmri.util.TimerUtil</a>
can help with these issues.</p>
<h3>Other Items of Interest</h3>
<p>Using a <a href=
"https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html">BlockingQueue</a>
from the <a href=
"https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html">java.util.concurrent</a>
package can remove the need to mess with thread synchronization and locking. That can be a
huge win!</p>
<p>The <a href=
"https://github.com/JMRI/JMRI/tree/master/java/test/jmri/util/ThreadingDemoAndTest.java">java/test/jmri/util/ThreadingDemoAndTest.java</a>
file is an example of using threads for join, interrupt, etc. It also includes a
BlockingQueue example.</p>
<p>The <a href=
"https://jmri.org/JavaDoc/doc/jmri/util/PropertyChangeEventQueue.html">jmri.util.PropertyChangeEventQueue</a>
class can handle listening to lots of NamedBeans without possibly missing or overlapping some
notifications. The <a href=
"https://github.com/JMRI/JMRI/tree/master/java/src/jmri/jmrit/automat/Siglet.java">jmri.jmrit.automat.Siglet</a>
class is an example of using this.</p>
<h2>Debugging</h2>
<a id="dump"></a>
<h3>Getting a thread-dump</h3>
When debugging threading issues, it can be helpful to get a "thread dump".
<p>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</p>
<ol>
<li>In the upper left, double-click the line that shows your running JMRI instance. (You
might have to open the "Local" selector to see it) If should say something about PanelPro
or DecoderPro. It'll take a few seconds, but then the right side will open with new
information.</li>
<li>Select the "Threads" tab in the middle-upper on the right side.</li>
<li>Click the "Thread Dump" button in upper right.</li>
<li>A new tab will open with the dump. There's no direct way to save that, but you can copy
and paste those contents into an editor to save them or an email to send them.</li>
</ol>
<p>If for some reason you can't use jvisualvm, then:</p>
<ul>
<li>On Linux and macOS when running from a command line, type control-backslash</li>
<li>On Linux and macOS when running detached, e.g. from startup icon,
<ul>
<li>Get the process ID (PID) from e.g. ps or Activity Monitor (under application name,
e.g. PanelPro)<br>
<code>ps | grep java | grep apps | awk '{print $1}'<br></code></li>
<li>and then send a QUIT signal to that process. E.g., if it's 6190,<br>
<div class="wide">
<code>kill -s QUIT 6190<br></code>
</div>
or you can combine them:
<div class="wide">
<code>kill -s QUIT `ps | grep java | grep apps | awk '{print $1}'`</code><br>
</div>
</li>
<li>Then look for the output in Console or ...</li>
</ul>
</li>
<li>If you're running under Netbeans or Eclipse, there are simple menu commands to do
this.</li>
</ul>
<p>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.</p>
<!--#include virtual="/help/en/parts/Footer.shtml" -->
</div>
<!-- closes #mainContent-->
</div>
<!-- closes #mBody-->
<script src="/js/help.js"></script>
</body>
</html>