Core Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Core Java Technologies Tech Tips for April 19, 2005. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE). This issue covers: * Thread Handling in Swing * Atomic Variables These tips were developed using Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp. This issue of the Core Java Technologies Tech Tips is written by John Zukowski, president of JZ Ventures, Inc. (http://www.jzventures.com). You can view this issue of the Tech Tips on the Web at http://java.sun.com/developer/JDCTechTips/2005/tt0419.html See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. For more Java technology content, visit these sites: java.sun.com - The latest Java platform releases, tutorials, and newsletters. java.net - A web forum for collaborating and building solutions together. java.com - Hot games, cool apps -- Experience the power of Java technology. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - THREAD HANDLING IN SWING To increase efficiency and decrease complexity, all Swing components are designed not to be thread-safe. This simply means that all access to Swing components needs to be done from a single thread. That thread is called the event-dispatch thread, and it isn't one you create yourself. If you are unsure that your executing code is in the event-dispatch thread, you can query the EventQueue class through its static isDispatchThread() method. Alternatively, you can query the SwingUtilities class through its static isEventDispatchThread() method. The isEventDispatchThread() method acts as a proxy to the isDispatchThread() method. To properly execute tasks on the event-dispatch thread, implement the Runnable interface and pass the tasks to the EventQueue class. Use the public static void invokeLater(Runnable runnable) method of EventQueue if you need to execute a task on the event-dispatch thread, but you don't need any results and you don't care when the task finishes. However, if you can't continue what you're doing until the task completes and returns a value, use the public static void invokeAndWait(Runnable runnable) method of EventQueue. With invokeAndWait(Runnable runnable), you need to provide the code to get the return value -- it is not returned by the invokeAndWait() method. If you're familiar with the SwingUtilities class, you know that it too has invokeLater() and invokeAndWait() methods. However, those two methods simply wrap the call to the EventQueue versions. So, it's better to directly call the EventQueue versions. You need to access Swing components from the event-dispatch thread for both realized (visible) and unrealized (invisible) components. It might seem reasonable to access unrealized components from a thread other than the event-dispatch thread. However, because building a Swing GUI can trigger notification of listeners (such as for a property change event or when adding an ancestor component), and that notification is on the event-dispatch thread, it is always best to access Swing components from the event-dispatch thread. This requirement of all access on the event-dispatch thread makes it interesting to create Swing programs. That's because the first things the main() method of a program does is create a Runnable object, create a JFrame, and put of all the components into that frame: Runnable runnable = new Runnable() { public void run() { // build screen } } EventQueue.invokeLater(runnable); Here's what one such program looks like. It creates a frame with a button. When the button is selected, it prints the message "I was selected." import javax.swing.*; import java.awt.*; import java.awt.event.*; public class ButtonSample { public static void main(final String args[]) { Runnable runner = new Runnable() { public void run() { String title = args.length == 0 ? "Hello, World" : args[0]; JFrame frame = new JFrame(title); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton button = new JButton("Select Me"); // Define ActionListener ActionListener actionListener = new ActionListener() { public void actionPerformed( ActionEvent actionEvent) { System.out.println("I was selected."); } }; // Attach Listeners button.addActionListener(actionListener); frame.add(button, BorderLayout.SOUTH); frame.setSize(300, 100); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } } Notice that the title for the frame is supplied in the command line. Because the Runnable object is creating another class, you have to declare the parameter to the main method as final to access the command-line arguments: public static void main(final String args[]) { If you forget this, and access args from the inner class, you get a compile time error message: ButtonSample.java:9: local variable args is accessed from within inner class; needs to be declared final String title = args.length == 0 ? "Hello, World" : args[0]; ^ ButtonSample.java:9: local variable args is accessed from within inner class; needs to be declared final "Hello, World" : args[0]; ^ 2 errors To avoid the possibility of threading problems when working with Swing interfaces, be sure all access is passed through the event-dispatch thread. For long running tasks, you can fork a new thread. If you choose to use invokeAndWait() instead of invokeLater(), the calling method blocks until the executing thread finishes, and returns control to the caller. In other words, you should only use invokeAndWait() from a thread other than the event dispatch thread. Also if you use invokeAndWait(), when execution returns to the calling thread, you can get a "return value" from a place that both threads know about. Because the calling thread is blocked, no synchronization is necessary. For more information about using threads with Swing, see the How to Use Threads trail (http://java.sun.com//tutorial/uiswing/misc/threads.html) in the Java Tutorial. The trail includes a description of a non-standard helper class called SwingWorker. If you use the worker class, make sure you use version 3 (also known as, SwingWorker 3), from February 2000. Earlier versions were buggy. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ATOMIC VARIABLES Three previous Tech Tips covered aspects of the new concurrency utility support in JDK 5.0. The February 16, 2005 tip, "Getting to Know Synchronizers" (http://java.sun.com/developer/JDCTechTips/2005/tt0216.html#1), discussed synchronizers. The November 16, 2004 Tech Tip, "Pooling Threads to Execute Short Tasks" (http://java.sun.com/developer/JDCTechTips/2004/tt1116.html#2), investigated thread pools. And the October 19, 2004 Tech Tip, "Queues and Delayed Processing" (http://java.sun.com/developer/JDCTechTips/2004/tt1019.html#1), explored the concurrent collections, including the blocking queue. The following tip examines another facet of the new concurrency support in JDK 5.0: atomic variables. You should be aware of the risks of sharing variables across different threads. It's important that you restrict access to shared variables by ensuring that only one thread changes a shared variable at a time. This is typically done by wrapping critical code sections with a synchronized block, such that only one thread is in a protected section at time. This technique works well, however synchronizing code adds to the runtime costs of your program. It takes time to get the synchronized lock, modify the variable, and then release the lock. It is quicker if you can skip the use of locks for simple variable updates, or simply have lock-free algorithms to begin with. But, you can't just remove the synchronized block without replacing it with something else. JDK 5.0 offers a way to meet these needs through the new java.util.concurrent.atomic package. The classes in the package allow you to atomically access variables of the designated type. They also offer methods for atomic get-and-set type operations. The package includes an AtomicInteger class for atomically updating integer values, AtomicLong for atomically updating long values, AtomicBoolean for basic boolean operations, and AtomicReference for atomic object comparisons and settings. There are also classes for special handling of arrays: AtomicIntegerArray, AtomicLongArray, and AtomicReferenceArray. To get started with atomic variables, let take a look at the AtomicInteger class. Essentially, this class works like a wrapped integer value. You get the value with the class's get() method, and set it with the set() method. You can also get and set the value in one step with the getAndSet() method -- this eliminates any risk of another thread changing the value between your call to get and your call to set. The basic get and set operations on an integer work as follows, where myVariable is the variable to manipulate: // Save off old value int oldValue = myVariable; // Change to new value myVariable = oldValue + 1; If you don't put these lines of code into a synchronized block, it is possible for the thread scheduler to interrupt in the middle of the two statements. If so, the change to myVariable happens on the original value of myVariable, not the updated version. There is a similar problem in using the ++ auto-increment operator. Short of putting the ++ usage in a synchronized block, there's no way to ensure that the auto-increment operation is atomic. To prevent these problems, use one of the new atomic methods in AtomicInteger. The methods let you combine set or get operations with one of several different methods: o addAndGet() o getAndAdd() o decrementAndGet() o getAndDecrement() o incrementAndGet() o getAndIncrement() o getAndSet() Why two versions for most of these methods? When "get" is first, the value returned is the original value. When "get" is second, the value returned is the new, adjusted value. So, for an AtomicInteger with a value of 10, getAndIncrement() returns 10, and incrementAndGet() returns 11. In both cases, the value of the AtomicInteger is 11 after the call. Another method in AtomicInteger worth mentioning is compareAndSet(int expect, int update). This method allows you to check if the value of the AtomicInteger is the expected value, and if it is, change the AtomicInteger to the new updated value. In fact, nearly all the methods previously mentioned are internally implemented with compareAndSet(). To demonstrate the value of the classes in the atomic package, consider the following. Say you had a property that was protected by a synchronized setter/getter pair of methods: public class MyLong1 { private long seed; public synchronized void setSeed(long seed) { this.seed = seed; } public synchronized long getSeed() { return seed; } } With the use of AtomicLong and its related classes, you can change this to use an unsynchronized version: public class MyLong2 { private AtomicLong seed; public void setSeed(long seed) { this.seed.set(seed); } public long getSeed() { return seed.get(); } } Notice that the assignment statement in the setter method changed to a call to the set() method of AtomicLong, and the getter method calls the get() method of AtomicLong. The use of AtomicLong here removes the need to synchronize the methods. In the specific case of the java.util.Random class, the setSeed() method is still synchronized due to other aspects of the method, not for the benefit of the seed property. As a simple rule, if you create synchronized blocks for accessing variables of type int, long, or boolean, consider swapping the synchronized block for an atomic variable. For more complex types, create your own synchronized type with the help of the AtomicReference class. For more information about the atomic package, see the javadoc for java.util.concurrent.atomic (http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/package-summary.html). . . . . . . . . . . . . . . . . . . . . . . . IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://www.sun.com/privacy/ http://developers.sun.com/dispatcher.jsp?uid=6910008 * FEEDBACK Comments? Please enter your feedback on the Tech Tips at: http://developers.sun.com/contact/feedback.jsp?category=sdn * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other JDC publications: - Go to the Sun Developer Network - Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Submit". - To unsubscribe, go to the Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Submit". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the Core Java Technologies Tech Tips archives at: http://java.sun.com/developer/TechTips/index.html - COPYRIGHT Copyright 2005 Sun Microsystems, Inc. All rights reserved. 4150 Network Circle, Santa Clara, California 95054 USA. This document is protected by copyright. For more information, see: http://java.sun.com/developer/copyright.html Core Java Technologies Tech Tips April 19, 2005 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.