| By Neal Ford | Article Rating: |
|
| May 1, 2001 12:00 AM EDT | Reads: |
18,841 |
Remember the old axiom, Be careful what you ask for, you just might get it?
That's what happened with the Abstract Windowing Toolkit (AWT), GUI controls, and threading. Developers were tired of always worrying about multithreaded access to GUI elements, so it sounded like a good idea to create an application framework that was always thread-safe.
What do we mean by thread-safe? Two separate threads of execution can access the control at the same time without the developer having to worry about the threads interfering with one another. AWT made this possible...and was consequently very sluggish. The original designers of Java built a lot of thread safety into the language and its libraries. For example, the collections classes from the original JDK (Vector and Hashtable) are always thread-safe. However, that safety comes at a cost. Because there's a great deal of overhead necessary to build thread-safe artifacts, they tend to be much slower than nonthread-safe alternatives. This is true of the collections classes (which is why we now have ArrayList and Hashmap, the nonthread-safe alternatives) and Swing.
When it came time to build JFC and Swing, one of the design decisions was that thread safety would be eschewed in favor of speed. This doesn't mean the controls can never be accessed from multiple threads, but the developer is now responsible for adding code to ensure that no ill effects occur. This article shows how to build thread-safe GUIs in Swing. First, however, I'll show what happens if you don't take care of threading.
Thread Collisions
Consider the application shown in Figure 1. It's a simple list box whose items are updated via a thread. For the first version of this example, no thread safety is built into the code (see Listing 1).
As you can see, the thread updates the contents of the list box continuously. What's bad about this? When it runs, you get the result shown in Figure 1. As the thread updates the list box, the drawing thread in Swing also accesses the elements to keep the GUI representation updated. Because both threads can access the content at the same time, the worker thread can pull items off the content at the same time the drawing thread is accessing them for display. This causes the cascading series of exceptions you see in the figure.
How can this be prevented? By using one of the static methods in the EventQueue class - invokeLater() or invokeAndWait(). These methods were originally in the SwingUtilities class (in JDK 1.1.x) and are now accessible from either class. These methods can take a thread as their parameter and are responsible for executing the thread that's passed to them in sync with the main Swing thread. These two methods determine how you want the update to occur. The invokeLater() method returns immediately, placing the update code in the regular event queue of Swing. This means the updating will take place as soon as possible, but it doesn't make your code wait for the update to occur. There may be situations in which you want your code to wait until the update has occurred. The invokeAndWait() method won't return until the update is complete. These methods are the secret to handling graceful threaded Swing code.
Listing 2 demonstrates how to solve the original problem. I've added code to call invokeLater() to the code that must update the Swing control's contents. Now when the application runs, no exceptions occur because both the update and drawing threads are no longer in conflict. The pertinent change to the code appears in the body of the run() method of the WorkerThread class and is shown here:
EventQueue.invokeLater(new Runnable() {This is a common technique for updating Swing controls from within a thread. A new anonymous class that implements the Runnable interface is created. The code in the run() method is the code placed in the main event thread in Swing. Notice that this is the same code used in the previous example to update the list box's model - the difference here is the call to EventQueue.invokeLater().
public void run() {
if (model.contains(i))
model.removeElement(i);
else
model.addElement(i);
}
});
Progress for Long-Running Processes
Here's a practical example of the need to have a thread update in real time. A common chore in applications is to show the users the progress of some long-running process. There are two ways to create such a process. If coded directly into the application (i.e., not in a thread), the application can't show progress because the process hasn't finished yet. In other words, if you do the work in an event handler, you're occupying the main event thread. If the process was spawned in a thread, the thread safety of the GUI must be considered. The second solution is the only real choice if you want to provide feedback, and armed with the EventQueue methods listed above, it's easy to accommodate.
For this sample application I copy a collection of files from one location to another. This is a classic example of a process for which you want to provide feedback. This application copies all the source files from the Java SwingSet2 demo to the temp directory (hey, I had to copy something from somewhere to somewhere else, didn't I?). As you can see in Figure 2, the user interface provides feedback on the percentage of completion for the two tasks. First, the application deletes the files from the target directory (if they've been previously copied there) and then copies the source files to the target.
Because there are two threads with common traits at work in this application, I first declared an abstract thread class to encapsulate the similarities. This class appears in Listing 3. Two characteristics are required from each thread. First, they must have a reference to the frame class to access the UI widgets to notify them of the progress. Second, a terminate request flag will appear in this base class. As you probably know, the thread stop() method has been deprecated in Java 2 because it can lead to undesirable situations. Now, to be able to stop a thread, you must provide a suicide watch flag - not so much a command to stop as a request that the thread commit suicide. Both worker threads subclass the abstract WorkerThread class.
The delete thread appears in Listing 4. One item to note in both threads is the manner in which they interact with the frame class. The thread doesn't directly access the JProgressBar on the frame. Instead, frame methods are called to handle the actual initialization and updating. The frame's initCopyProgress() method is shown here:
void initCopyProgress(int numFiles) {The updateCopyProgress() method handles the updating of the progress bar's progress.
jprgrsbrCopy.setMaximum(numFiles);
jprgrsbrCopy.setMinimum(0);
}
void updateCopyProgress() {
jprgrsbrCopy.setValue(
jprgrsbrCopy.getValue() + 1);
}
Making the frame's methods update the progress bar is a good idea so the thread doesn't have too much knowledge of how the user interface is handling the progress mechanism. The UI could now change to incorporate a gauge or some other progress technique without affecting the threads. The remainder of the DeleteThread deletes the files in the target directory. Notice the call to EventQueue.invokeLater() to update the frame. The last act of the DeleteThread is to instantiate and call the CopyThread (see Listing 5). Both threads could have been created at the same time and the CopyThread made to wait on the DeleteThread. However, because these two operations must run serially, it makes sense to spawn one from the other.
The CopyThread performs many of the same housekeeping operations as the DeleteThread. Most of the code in the CopyThread concerns itself with copying files, which is not relevant to this discussion. For our purposes note the call to update the user interface in a call to EventQueue. invokeLater().
Building user interfaces that gracefully spawn threads and in turn report progress along the way is easy once you understand the requirements built into the Swing application framework. Now we have a rich user interface library that offers better speed and capabilities than the old one. When necessary, the developer can make it thread-safe to give it the capabilities of the old AWT framework without the disadvantages.
Published May 1, 2001 Reads 18,841
Copyright © 2001 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Neal Ford
Neal is the Vice-President of Technology at
the DSW Group. He is also the designer and
developer of applications, instructional
materials, magazine articles, video presentations,
and author of the books Developing with Delphi:
Object-Oriented Techniques and JBuilder 3
Unleashed. Neal has also been the featured
speaker for the Borland Delphi/C++Builder/JBuilder
World Tours and has spoken extensively at annual
Borland Developers Conferences worldwide.
He can be reached at nford@thedswgroup.com
or by calling The DSW Group at 800-356-9644.
- Patterns for Building High Performance Applications
- It's the Java vs. C++ Shootout Revisited!
- Asynchronous Logging Using Spring
- Java for Programmers (2nd Edition)
- Cross-Platform Mobile Website Development – a Tool Comparison
- Three Buzzwords That Every CIO Hears but One They Should Listen To
- Write Once Run Anywhere or Cross Platform Mobile Development Tools
- Immersing into JavaScript Frameworks
- Workday Reportedly Prepping to Go Public
- Cloud Expo New York: The Java EE 7 Platform - Developing for the Cloud
- Book Review: Sams Teach Yourself Java in 24 Hours
- OpenOffice.com Lives
- Book Excerpt: Introducing HTML5
- Adobe Sends Flex to the Apache Foundation
- Five Years Waiting for JRE 7: Is It Justified? (Part 1)
- Book Excerpt: Java Application Profiling Tips and Tricks
- i-Technology in 2012: Five Industry Predictions
- Patterns for Building High Performance Applications
- It's the Java vs. C++ Shootout Revisited!
- OpenXava 4.3: Rapid Java Web Development
- The Next Web Architecture
- Asynchronous Logging Using Spring
- Java for Programmers (2nd Edition)
- Is Write Once Run Anywhere Ever Going to Be a Reality?
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- JavaServer Faces (JSF) vs Struts
- The i-Technology Right Stuff
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- Why Do 'Cool Kids' Choose Ruby or PHP to Build Websites Instead of Java?
- What's New in Eclipse?
- i-Technology Predictions for 2007: Where's It All Headed?


















