/[debian]/libspin-java/branches/upstream/current/src/site/xdoc/problem.xml
ViewVC logotype

Annotation of /libspin-java/branches/upstream/current/src/site/xdoc/problem.xml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 680 - (hide annotations)
Sun Apr 1 14:25:01 2007 UTC (14 years, 4 months ago) by gregoa
File MIME type: text/xml
File size: 19865 byte(s)
[svn-upgrade] Integrating new upstream version, libspin-java (1.5)
1 gregoa 680 <document>
2     <properties>
3     <author email="sven@users.sourceforge.net">Sven Meier</author>
4     <title>Spin your Swing</title>
5     </properties>
6     <body>
7     <section name="The Problem">
8     <subsection name="Standard Swing">
9     <p>
10     In this section we take a look at a naive <acronym title="Graphical user interface">GUI</acronym> implementation that shows how Swing freezes in case the application programmer doesn't take any special actions against it. We also describe the problem of calls into Swing components triggered by any other thread than the event dispatch thread (EDT). <br/>
11     Swing is not designed for multi threading, so let us first recall the single threading rule of every Swing <acronym title="Graphical user interface">GUI</acronym>:
12     </p>
13     <blockquote>
14     <cite>Access to a (visible) Swing component has to occur in the event dispatch thread.</cite>
15     </blockquote>
16     <p>
17     The <acronym title="Event Dispatch Thread">EDT</acronym> is responsible to process all <acronym title="Graphical user interface">GUI</acronym> related events, e.g. notifying listeners of user input, repainting dirty regions or updated areas. All these events are enqueued and treated sequentially - if one of them takes a long time to be processed, all further events will have to wait.
18     </p>
19     <p>
20     Throughout the next sections we'll put the code of a Swing <acronym title="Graphical user interface">GUI</acronym> component and a non visual bean side by side. The bean encapsulates an extensive calculation that is used by multiple threads. Code run on the <acronym title="Event Dispatch Thread">EDT</acronym> is <span class="onEDT">shown in green</span> and code called by any other thread is <span class="offEDT">shown in red</span>.
21     </p>
22     <p>
23     As you can see in the following code, <code>GUI.java</code> calls the method <code>getValue()</code> on the bean when an action is performed. The <acronym title="Event Dispatch Thread">EDT</acronym> is routed from the <acronym title="Graphical user interface">GUI</acronym> to the bean. While it is performing its calculations no further swing events can be processed - the <acronym title="Graphical user interface">GUI</acronym> freezes.<br/>
24     One of these queued events is the repaint of the label triggered by <code>label.setText("...")</code>. When <code>getValue()</code> returns, the text of the label is changed again before the previous text was painted. So in fact <code>"..."</code> is never seen:
25     </p>
26    
27     <!--
28     public void actionPerformed(ActionEvent e)
29     {
30     label.setText("...");
31     label.setText(bean.getValue());
32     }
33    
34     public void propertyChange(PropertyChangeEvent ev)
35     {
36     label.setText((String)ev.getNewValue());
37     }
38     -->
39     <div class="java left">
40     <h3 class="java">GUI.java</h3>
41     <code class="java"><span class="java4">public </span><span class="java9">void </span><span class="java10">actionPerformed</span><span class="java8">(</span><span class="java10">ActionEvent e</span><span class="java8">)<br />
42     {<br />
43     </span><span class="onEDT">&#xA0; <span class="java10">label.setText</span><span class="java8">(</span><span class="java5">&#34;...&#34;</span><span class="java8">)</span><span class="java10">;<br />
44     &#xA0; label.setText</span><span class="java8">(</span><span class="java10">bean.getValue</span><span class="java8">())</span><span class="java10">;<br />
45     </span></span><span class="java8">}<br />
46     <br />
47     <br />
48     </span><span class="java4">public </span><span class="java9">void </span><span class="java10">propertyChange</span><span class="java8">(</span><span class="java10">PropertyChangeEvent ev</span><span class="java8">)<br />
49     {<br />
50     </span><span class="offEDT">&#xA0; <span class="java10">label.setText</span><span class="java8">((</span><span class="java10">String</span><span class="java8">)</span><span class="java10">ev.getNewValue</span><span class="java8">())</span><span class="java10">;<br />
51     </span></span><span class="java8">}</span>
52     <br />&#xA0;</code></div>
53    
54     <!--
55     public String getValue()
56     {
57     String value;
58     // extensive calculation
59     return value;
60     }
61    
62     public void setValue(String value)
63     {
64     this.value = value;
65     firePropertyChange(value);
66     }
67     -->
68     <div class="java right">
69     <h3 class="java">BeanImpl.java</h3>
70     <code class="java"><span class="java4">public </span><span class="java10">String getValue</span><span class="java8">()<br />
71     {<br />
72     </span><span class="onEDT">&#xA0; <span class="java10">String value;<br />
73     &#xA0; </span><span class="java3">// extensive calculation<br />
74     &#xA0; </span><span class="java4">return </span><span class="java10">value;<br />
75     </span></span><span class="java8">}<br />
76     <br />
77     </span><span class="java4">public </span><span class="java9">void </span><span class="java10">setValue</span><span class="java8">(</span><span class="java10">String value</span><span class="java8">)<br />
78     {<br />
79     </span><span class="offEDT">&#xA0; <span class="java4">this</span><span class="java10">.value = value;<br />
80     &#xA0; firePropertyChange</span><span class="java8">(</span><span class="java10">value</span><span class="java8">)</span><span class="java10">;<br />
81     </span></span><span class="java8">}</span></code></div>
82    
83     <div class="clear"/>
84    
85     <p>
86     What happens if <code>setValue()</code> is invoked on the bean on another thread. The listeners are notified (the class <code>GUI</code> implements <code>java.beans.PropertyChangeListener</code> and is registered as a listener for changes of the bean), triggering a call to <code>propertyChange()</code> on the <acronym title="Graphical user interface">GUI</acronym>. The text of the label is altered on the calling thread, violating the Swing threading rule.
87     </p>
88     <p>
89     The color distribution gives a hint where to search for problems of this implementation:<br/>
90     <span class="onEDT">Green lines</span> of code in <code>BeanImpl.java</code> result in a <acronym title="Graphical user interface">GUI</acronym> freeze, <span class="offEDT">red lines</span> in <code>GUI.java</code> show a violation to the Swing threading rule.
91     </p>
92     </subsection>
93     <subsection name="Working Thread">
94     <p>
95     One obvious solution to the problems seen in the previous section is to shift the invocation of <code>getValue()</code> from the <acronym title="Event Dispatch Thread">EDT</acronym> to a separate thread. When this method returns we must not propagate the result to a Swing component though. We have to return control to the <acronym title="Event Dispatch Thread">EDT</acronym> instead. This can be achieved via <code>SwingUtilities.invokeLater()</code> which will use our Runnable to correctly change the label's text on the <acronym title="Event Dispatch Thread">EDT</acronym>:
96     </p>
97    
98     <!--
99     public void actionPerformed(ActionEvent e)
100     {
101     label.setText("...");
102     new Thread(new Runnable()
103     {
104     public void run()
105     {
106     final String value = bean.getValue();
107     SwingUtilities.invokeLater(new Runnable()
108     {
109     public void run()
110     {
111     label.setText(value);
112     }
113     });
114     }
115     }).start();
116     }
117    
118     public void propertyChange(final PropertyChangeEvent ev)
119     {
120     SwingUtilities.invokeLater(new Runnable()
121     {
122     public void run()
123     {
124     label.setText((String)ev.getNewValue());
125     }
126     });
127     }
128     -->
129     <div class="java left">
130     <h3 class="java">GUI.java</h3>
131     <code class="java"><span class="java4">public </span><span class="java9">void </span><span class="java10">actionPerformed</span><span class="java8">(</span><span class="java10">ActionEvent e</span><span class="java8">)<br />
132     {<br />
133     </span><span class="onEDT">&#xA0; <span class="java10">label.setText</span><span class="java8">(</span><span class="java5">&#34;...&#34;</span><span class="java8">)</span><span class="java10">;<br />
134     &#xA0; </span><span class="java4">new </span><span class="java10">Thread</span><span class="java8">(</span><span class="java4">new </span><span class="java10">Runnable</span><span class="java8">()<br />
135     &#xA0; {<br /></span></span>
136     &#xA0;&#xA0;&#xA0; <span class="java4">public </span><span class="java9">void </span><span class="java10">run</span><span class="java8">()<br />
137     &#xA0;&#xA0;&#xA0; {</span><br />
138     <span class="offEDT">&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java4">final </span><span class="java10">String value = bean.getValue</span><span class="java8">()</span><span class="java10">;<br />
139     &#xA0;&#xA0;&#xA0;&#xA0;&#xA0; SwingUtilities.invokeLater</span><span class="java8">(</span><span class="java4">new </span><span class="java10">Runnable</span><span class="java8">()<br />
140     &#xA0;&#xA0;&#xA0;&#xA0;&#xA0; {</span></span><br />
141     &#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java4">public </span><span class="java9">void </span><span class="java10">run</span><span class="java8">()<br />
142     &#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; {</span><br />
143     <span class="onEDT">&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java10">label.setText</span><span class="java8">(</span><span class="java10">value</span><span class="java8">)</span><span class="java10">;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;</span></span><br />
144     &#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java8">}</span><br />
145     <span class="offEDT">&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java8">})</span><span class="java10">;</span></span><br />
146     &#xA0;&#xA0;&#xA0; <span class="java8">}<br /></span>
147     <span class="onEDT">&#xA0; <span class="java8">}})</span><span class="java10">.start</span><span class="java8">()</span><span class="java10">;<br />
148     </span></span><span class="java8">}<br />
149     <br />
150     </span><span class="java4">public </span><span class="java9">void </span><span class="java10">propertyChange</span><span class="java8">(</span><span class="java4">final </span><span class="java10">PropertyChangeEvent ev</span><span class="java8">)<br />
151     {<br />
152     </span><span class="offEDT">&#xA0; <span class="java10">SwingUtilities.invokeLater</span><span class="java8">(</span><span class="java4">new </span><span class="java10">Runnable</span><span class="java8">()<br />
153     &#xA0; {<br /></span></span>
154     &#xA0;&#xA0;&#xA0; <span class="java4">public </span><span class="java9">void </span><span class="java10">run</span><span class="java8">()<br />
155     &#xA0;&#xA0;&#xA0; {</span><br />
156     <span class="onEDT">&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java10">label.setText</span><span class="java8">((</span><span class="java10">String</span><span class="java8">)</span><span class="java10">ev.getNewValue</span><span class="java8">())</span><span class="java10">;</span></span><br />
157     &#xA0;&#xA0;&#xA0; <span class="java8">}</span><br />
158     <span class="offEDT">&#xA0; <span class="java8">})</span><span class="java10">;</span></span><br />
159     <span class="java8">}</span></code></div>
160    
161     <!--
162     public String getValue()
163     {
164     String value;
165     // extensive calculation
166     return value;
167     }
168    
169     public void setValue(String value)
170     {
171     this.value = value;
172     firePropertyChange(value);
173     }
174     -->
175     <div class="java right">
176     <h3 class="java">BeanImpl.java</h3>
177     <code class="java"><span class="java4">public </span><span class="java10">String getValue</span><span class="java8">()<br />
178     {</span><br />
179     <span class="offEDT">&#xA0; <span class="java10">String value;<br />
180     &#xA0; </span><span class="java3">// extensive calculation<br />
181     &#xA0; </span><span class="java4">return </span><span class="java10">value;</span></span><br />
182     <span class="java8">}<br />
183     <br />
184     <br />
185     <br />
186     <br />
187     <br />
188     <br />
189     <br />
190     <br />
191     <br />
192     <br />
193     <br />
194     <br />
195     <br />
196     </span><span class="java4">public </span><span class="java9">void </span><span class="java10">setValue</span><span class="java8">(</span><span class="java10">String value</span><span class="java8">)<br />
197     {</span><br />
198     <span class="offEDT">&#xA0; <span class="java4">this</span><span class="java10">.value = value;<br />
199     &#xA0; firePropertyChange</span><span class="java8">(</span><span class="java10">value</span><span class="java8">)</span><span class="java10">;</span></span><br />
200     <span class="java8">}</span>
201     <br />
202     <br />
203     <br />
204     <br />
205     <br />
206     &#xA0;</code></div>
207    
208     <div class="clear"/>
209    
210     <p>
211     Now what happens if the bean informs the <acronym title="Graphical user interface">GUI</acronym> about a value-change triggered by another thread? In <code>propertyChange()</code> we pass a runnable to the <acronym title="Event Dispatch Thread">EDT</acronym> via <code>SwingUtiltites.invokeLater()</code> that can safely alter the label.
212     </p>
213     <p>
214     Let's take a look at the colors once again: In <code>BeanImpl.java</code> there are only <span class="offEDT">red lines</span> - so we achieved a non freezing <acronym title="Graphical user interface">GUI</acronym>. <code>GUI.java</code> has <span class="onEDT">almost all lines in green</span>. Since we restrict changes to Swing components to these <span class="onEDT">green lines</span> we are honouring the Swing threading rule too.<br/>
215     But the <span class="offEDT">red lines </span> in <code>GUI.java</code> make things difficult: The programmer of this class always has to know which thread is stepping through what part of the code - without any visual help of thread-coloring. Any mistake reintroduces the problems mentioned above.
216     </p>
217     </subsection>
218     <subsection name="Swingworker">
219     <p>
220     SwingWorker is a utility class that aims to ease the efforts to write a non-freezing <acronym title="Graphical user interface">GUI</acronym>. Although not included in the standard Java distribution it is maintained by the Swing team and downloadable at <a href="http://www.theswingconnection.com">The Swing Connection</a>.
221     </p>
222     <p>
223     As you can see in the following example a SwingWorker removes some of the visual clutter seen in the previous section. To use it you have to subclass it, placing extensive calculations into method <code>construct()</code>. In <code>finished()</code> you can alter the label because this method is called on the <acronym title="Event Dispatch Thread">EDT</acronym>. This is similar to our previous solution but this time the threading is handled by the superclass:
224     </p>
225    
226     <!--
227     public void actionPerformed(ActionEvent e)
228     {
229     label.setText("...");
230     new SwingWorker()
231     {
232     public Object construct()
233     {
234     return bean.getValue();
235     }
236    
237     public void finished()
238     {
239     label.setText((String)getValue());
240     }
241     }).start();
242     }
243    
244     public void propertyChange(final PropertyChangeEvent ev)
245     {
246     SwingUtilities.invokeLater(new Runnable()
247     {
248     public void run()
249     {
250     label.setText((String)ev.getNewValue());
251     }
252     });
253     }
254     -->
255     <div class="java left">
256     <h3 class="java">GUI.java</h3>
257     <code class="java"><span class="java4">public </span><span class="java9">void </span><span class="java10">actionPerformed</span><span class="java8">(</span><span class="java10">ActionEvent e</span><span class="java8">)<br />
258     {</span><br />
259     <span class="onEDT">&#xA0; <span class="java10">label.setText</span><span class="java8">(</span><span class="java5">&#34;...&#34;</span><span class="java8">)</span><span class="java10">;<br />
260     &#xA0; </span><span class="java4">new </span><span class="java10">SwingWorker</span><span class="java8">()<br />
261     &#xA0; {</span></span><br />
262     &#xA0;&#xA0;&#xA0; <span class="java4">public </span><span class="java10">Object construct</span><span class="java8">()<br />
263     &#xA0;&#xA0;&#xA0; {</span><br />
264     <span class="offEDT">&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java4">return </span><span class="java10">bean.getValue</span><span class="java8">()</span><span class="java10">;</span></span><br />
265     &#xA0;&#xA0;&#xA0; <span class="java8">}<br />
266     <br />
267     &#xA0;&#xA0;&#xA0; </span><span class="java4">public </span><span class="java9">void </span><span class="java10">finished</span><span class="java8">()<br />
268     &#xA0;&#xA0;&#xA0; {</span><br />
269     <span class="onEDT">&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java10">label.setText</span><span class="java8">((</span><span class="java10">String</span><span class="java8">)</span><span class="java10">getValue</span><span class="java8">())</span><span class="java10">;</span></span><br />
270     &#xA0;&#xA0;&#xA0; <span class="java8">}</span><br />
271     <span class="onEDT">&#xA0; <span class="java8">})</span><span class="java10">.start</span><span class="java8">()</span><span class="java10">;</span></span><br />
272     <span class="java8">}<br />
273     <br />
274     </span><span class="java4">public </span><span class="java9">void </span><span class="java10">propertyChange</span><span class="java8">(</span><span class="java4">final </span><span class="java10">PropertyChangeEvent ev</span><span class="java8">)<br />
275     {<br />
276     </span><span class="offEDT">&#xA0; <span class="java10">SwingUtilities.invokeLater</span><span class="java8">(</span><span class="java4">new </span><span class="java10">Runnable</span><span class="java8">()<br />
277     &#xA0; {<br /></span></span>
278     &#xA0;&#xA0;&#xA0; <span class="java4">public </span><span class="java9">void </span><span class="java10">run</span><span class="java8">()<br />
279     &#xA0;&#xA0;&#xA0; {</span><br />
280     <span class="onEDT">&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <span class="java10">label.setText</span><span class="java8">((</span><span class="java10">String</span><span class="java8">)</span><span class="java10">ev.getNewValue</span><span class="java8">())</span><span class="java10">;</span></span><br />
281     &#xA0;&#xA0;&#xA0; <span class="java8">}</span><br />
282     <span class="offEDT">&#xA0; <span class="java8">})</span><span class="java10">;</span></span><br />
283     <span class="java8">}</span></code></div>
284    
285     <!--
286     public String getValue()
287     {
288     String value;
289     // extensive calculation
290     return value;
291     }
292    
293     public void setValue(String value)
294     {
295     this.value = value;
296     firePropertyChange(value);
297     }
298     -->
299     <div class="java right">
300     <h3 class="java">BeanImpl.java</h3>
301     <code class="java"><span class="java4">public </span><span class="java10">String getValue</span><span class="java8">()<br />
302     {</span><br />
303     <span class="offEDT">&#xA0; <span class="java10">String value;<br />
304     &#xA0; </span><span class="java3">// extensive calculation<br />
305     &#xA0; </span><span class="java4">return </span><span class="java10">value;</span></span><br />
306     <span class="java8">}<br />
307     <br />
308     <br />
309     <br />
310     <br />
311     <br />
312     <br />
313     <br />
314     <br />
315     <br />
316     <br />
317     <br />
318     </span><span class="java4">public </span><span class="java9">void </span><span class="java10">setValue</span><span class="java8">(</span><span class="java10">String value</span><span class="java8">)<br />
319     {</span><br />
320     <span class="offEDT">&#xA0; <span class="java4">this</span><span class="java10">.value = value;<br />
321     &#xA0; firePropertyChange</span><span class="java8">(</span><span class="java10">value</span><span class="java8">)</span><span class="java10">;</span></span><br />
322     <span class="java8">}
323     <br />
324     <br />
325     <br />
326     <br />
327     <br />&#xA0;</span></code></div>
328    
329     <div class="clear"/>
330    
331     <p>
332     The SwingWorker offers no support for our notification problem so we stick to our previous solution in <code>propertyChange()</code>.
333     </p>
334     <p>
335     What about the colors?<br/>
336     The situation hasn't really improved. The indentation of code was minimized but we still have <span class="offEDT">red lines</span> in <code>GUI.java</code>. So the problem above isn't resolved yet, we're still seeking a better <a href="solution.html">solution</a>.
337     </p>
338     </subsection>
339     </section>
340     </body>
341     </document>

  ViewVC Help
Powered by ViewVC 1.1.26