epubjs
Version:
Render ePub documents in the browser, across many devices
151 lines (133 loc) • 15.4 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Multithreading in Swing</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Multithreading in Swing"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-16-SECT-5"/>Multithreading in Swing</h1></div></div></div><p><a id="idx10971" class="indexterm"/>An important compromise was made early in the design of
Swing relating to speed, GUI consistency, and thread safety. To provide
maximum performance and simplicity in the common case, Swing does not
explicitly synchronize access to most Swing component methods. This means
that most Swing components are, technically, not threadsafe for
multithreaded applications. Now don’t panic: it’s not as bad as it sounds
because there is a plan. All event processing in AWT/Swing is handled by a
single system thread using a single system event queue. The queue serves
two purposes. First, it eliminates thread safety issues by making all GUI
modifications happen in a single thread. Second, the queue imposes a
strict ordering of all activity in Swing. Because painting is handled in
Swing using events, all screen updating is also ordered with respect to
all event handling.</p><p>What this means for you is that multithreaded programs need to be
careful about how they update Swing components after they are
<span class="emphasis"><em>realized</em></span> (added to a visible container). If you make
arbitrary modifications to Swing components from your own threads, you run
the risk of malformed rendering on the screen and inconsistent
behavior.</p><p>There are several conditions under which it is always safe to modify
a Swing component. First, Swing components can be modified before they are
realized. The term <span class="emphasis"><em>realized</em></span> originates from the days
when the component would have created its peer object. It is the point
when it is added to a visible container or when it is made visible in the
case of a window. Most of our examples in this book set up GUI components
in their <code class="literal">main()</code> method, add them to a
<code class="literal">JFrame</code>, and then, as their final
action, cause the <code class="literal">JFrame</code> to be
displayed using <code class="literal">setVisible()</code>. This
setup style is safe because components are not realized until the
container is made visible. Actually, that last sentence is not entirely
true. Technically, components can also be realized by the <code class="literal">JFrame() pack()</code> method. However, because no GUI
is shown until the container is made visible, it is unlikely that any GUI
activity can be mishandled.</p><p>Second, it’s safe to modify Swing components from code that is
already running from the system event handler’s thread. Because all events
are processed by the event queue, the methods of all Swing event listeners
are normally invoked by the system event-handling thread. This means that
event handler methods and, transitively, any methods called from those
methods during the lifetime of that call, can freely modify Swing GUI
components because they are already running in the system
event-dispatching thread. If unsure of whether some bit of code will ever
be called outside the normal event thread, you can use the static method
<code class="literal">SwingUtilities.isEventDispatchThread()</code>
to test the identity of the current thread. You can then perform your
activity using the event-queue mechanism we’ll talk about next.</p><p>Finally, Swing components can be safely modified when the API
documentation explicitly says that the method is threadsafe. Many
important methods of Swing components are explicitly documented as
threadsafe. These include the <code class="literal">JComponent
repaint()</code> and <code class="literal">revalidate()</code>
methods, many methods of Swing text components, and all listener add and
remove methods.</p><p>If you can’t meet any of the requirements for thread safety listed
previously, you can use a simple API to get the system event queue to
perform arbitrary activities for you on the event-handling thread. This is
accomplished using the <a id="I_indexterm16_id793484" class="indexterm"/><code class="literal">invokeAndWait()</code> or
<a id="I_indexterm16_id793495" class="indexterm"/><code class="literal">invokeLater()</code> static
methods of the <a id="I_indexterm16_id793506" class="indexterm"/><code class="literal">javax.swing.SwingUtilities</code> class:</p><div class="variablelist"><dl><dt><span class="term"><code class="literal">public static void invokeLater(Runnable
doRun)</code></span></dt><dd><p>Use this method to ask Swing to execute the <code class="literal">run()</code> method of the specified <code class="literal">Runnable</code>.</p></dd><dt><span class="term"><code class="literal">public static void
invokeAndWait(Runnable
doRun)throwsInterruptedException,InvocationTargetException</code></span></dt><dd><p>This method is just like <code class="literal">invokeLater()</code> except that it waits until
the <code class="literal">run()</code> method has completed
before returning.</p></dd></dl></div><p>You can put any activities you want inside a <code class="literal">Runnable</code> object and cause the event dispatcher
to perform them using these methods. Often you’ll use an inner class; for
example:</p><a id="I_16_tt1001"/><pre class="programlisting"> <code class="n">SwingUtilities</code><code class="o">.</code><code class="na">invokeLater</code><code class="o">(</code> <code class="k">new</code> <code class="n">Runnable</code><code class="o">()</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">run</code><code class="o">()</code> <code class="o">{</code>
<code class="n">MyComponent</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">false</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code> <code class="o">);</code></pre><p>Java 7 introduced the <a id="I_indexterm16_id793592" class="indexterm"/><code class="literal">SwingWorker</code> class to
assist in situations where you have a background process that needs to
update a Swing UI after the process is complete or incrementally as it’s
running. In the former case, it’s a simple matter of subclassing <code class="literal">SwingWorker</code>, and putting your long-running code
in <a id="I_indexterm16_id793613" class="indexterm"/><code class="literal">doInBackground()</code> and your
UI update code in <a id="I_indexterm16_id793624" class="indexterm"/><code class="literal">done()</code>.</p><a id="I_programlisting16_id793634"/><pre class="programlisting"> <code class="kn">package</code> <code class="n">learning</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.awt.BorderLayout</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.awt.event.ActionEvent</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.awt.event.ActionListener</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">MysteryOfTheUniverse</code> <code class="kd">extends</code> <code class="n">JFrame</code> <code class="o">{</code>
<code class="n">JTextArea</code> <code class="n">textArea</code><code class="o">;</code>
<code class="n">JButton</code> <code class="n">solveButton</code><code class="o">;</code>
<code class="kd">public</code> <code class="nf">MysteryOfTheUniverse</code><code class="o">()</code> <code class="o">{</code>
<code class="kd">super</code><code class="o">(</code><code class="s">"Mystery of the Universe Solver"</code><code class="o">);</code>
<code class="n">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code>
<code class="n">setSize</code><code class="o">(</code><code class="mi">300</code><code class="o">,</code> <code class="mi">300</code><code class="o">);</code>
<code class="n">textArea</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code>
<code class="n">solveButton</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JButton</code><code class="o">(</code><code class="s">"Solve Mystery"</code><code class="o">);</code>
<code class="n">solveButton</code><code class="o">.</code><code class="na">addActionListener</code><code class="o">(</code><code class="k">new</code> <code class="n">ActionListener</code><code class="o">()</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">actionPerformed</code><code class="o">(</code><code class="n">ActionEvent</code> <code class="n">ae</code><code class="o">)</code> <code class="o">{</code>
<code class="n">solveButton</code><code class="o">.</code><code class="na">setEnabled</code><code class="o">(</code><code class="kc">false</code><code class="o">);</code>
<code class="n">solveMysteryOfTheUniverse</code><code class="o">();</code>
<code class="o">}</code>
<code class="o">});</code>
<code class="n">add</code><code class="o">(</code><code class="n">solveButton</code><code class="o">,</code> <code class="n">BorderLayout</code><code class="o">.</code><code class="na">NORTH</code><code class="o">);</code>
<code class="n">add</code><code class="o">(</code><code class="k">new</code> <code class="n">JScrollPane</code><code class="o">(</code><code class="n">textArea</code><code class="o">));</code>
<code class="n">add</code><code class="o">(</code><code class="k">new</code> <code class="n">JButton</code><code class="o">(</code><code class="s">"Click me! I'm not blocking."</code><code class="o">),</code>
<code class="n">BorderLayout</code><code class="o">.</code><code class="na">SOUTH</code><code class="o">);</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">solveMysteryOfTheUniverse</code><code class="o">()</code> <code class="o">{</code>
<code class="o">(</code><code class="k">new</code> <code class="n">MysteryWorker</code><code class="o">()).</code><code class="na">execute</code><code class="o">();</code>
<code class="o">}</code>
<code class="kd">class</code> <code class="nc">MysteryWorker</code> <code class="kd">extends</code> <code class="n">SwingWorker</code><code class="o"><</code><code class="n">String</code><code class="o">,</code> <code class="n">Object</code><code class="o">></code> <code class="o">{</code>
<code class="nd">@Override</code>
<code class="kd">public</code> <code class="n">String</code> <code class="nf">doInBackground</code><code class="o">()</code> <code class="o">{</code>
<code class="c1">// Thinking for 4 seconds, but not blocking the UI</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">Thread</code><code class="o">.</code><code class="na">currentThread</code><code class="o">().</code><code class="na">sleep</code><code class="o">(</code><code class="mi">4000</code><code class="o">);</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code><code class="n">InterruptedException</code> <code class="n">ignore</code><code class="o">)</code> <code class="o">{}</code>
<code class="n">solveButton</code><code class="o">.</code><code class="na">setEnabled</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code>
<code class="k">return</code> <code class="s">"Egg salad"</code><code class="o">;</code>
<code class="o">}</code>
<code class="nd">@Override</code>
<code class="kd">protected</code> <code class="kt">void</code> <code class="nf">done</code><code class="o">()</code> <code class="o">{</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">textArea</code><code class="o">.</code><code class="na">setText</code><code class="o">(</code><code class="n">get</code><code class="o">());</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code><code class="n">Exception</code> <code class="n">ignore</code><code class="o">)</code> <code class="o">{}</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code><code class="o">[]</code> <code class="n">args</code><code class="o">)</code> <code class="o">{</code>
<code class="k">new</code> <code class="nf">MysteryOfTheUniverse</code><code class="o">().</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>When you click the <span class="guibutton">Solve</span>
button, the application spends four seconds solving the mystery of the
universe. If we weren’t using <code class="literal">SwingWorker</code>, the event dispatch thread would
block, making the <span class="guibutton">Click Me</span> button
at the bottom of the screen unclickable. Thanks to <code class="literal">SwingWorker</code>, the UI remains usable during the
time the background task is executing. Don’t trust us. Try it!</p><p><code class="literal">SwingWorker</code> can be used in more
complex situations such as incremental updates of a progress bar. See
<code class="literal">SwingWorker</code>’s JavaDoc introduction for
an example of this usage.</p><p>You may find that you won’t have to use the event dispatcher or
<code class="literal">SwingWorker</code> in simple GUI applications
because most activity happens in response to user interface events where
it is safe to modify components. However, consider these caveats when you
create threads to perform long-running tasks such as loading data or
communicating over the network.<a id="I_indexterm16_id793714" class="indexterm"/></p></div></body></html>