Core Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Core Java Technologies Tech Tips for July 27, 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: * Swing "Urban Legends" * From Runtime.exec() to ProcessBuilder 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/tt0727.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. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SWING "URBAN LEGENDS" Developers who work with Swing components often hear about certain ways of doing things that they assume are the right ways to work with Swing. Like "urban legends" that purport to be accounts of actual events but never really happened, some of these Swing techniques are incorrect. In this tip, you'll learn about a number of these Swing urban legends -- approaches that will hinder the performance of your Swing applications. In some cases, the performance reduction might only be nanoseconds, but if you want the best performance profile, even cutting a handful of nanoseconds adds up over time. Here are three Swing urban legends: o Create threads for long tasks from the event dispatch thread. o Use SwingUtilities for running tasks on the event dispatch thread. o Synchronize methods for synchronization. Create Threads for Long Tasks From the Event Dispatch Thread Here's the situation: you want to spin off a thread from the event queue to do a long task. If your event handler needs to do a long task, you don't want to block the event thread. So you create a new thread for the long task, and call invokeLater() when the task is done to handle the results on the event thread. Here's the typical usage pattern: public void actionPerformed(ActionEvent e) { Runnable longTask = new Runnable() { public void run() { // Run task to do long stuff ... // long task // Update Swing component when done Runnable awtTask = new Runnable() { public void run() { // Update Swing component } } EventQueue.invokeLater(awtTask); } }; Thread t = new Thread(longTask); t.start(); } This is a common pattern: run long tasks off the event dispatch thread, then update the Swing component after the long task is done. It seems like the right thing to do, but it isn't. If you follow this pattern, you'll run across a problem that could cause your program to behave badly. When a new thread is created, it retains the thread priority of the creating thread. Because the event thread typically runs at a higher level than normal threads, threads created from the event thread inherit the higher priority. Here is a simple program, Threads, that demonstrates the thread priorities: import java.awt.*; public class Threads { public static void main(String args[]) { System.out.println("Main Thread priority: " + Thread.currentThread().getPriority()); Runnable runner = new Runnable() { public void run() { System.out.println("Event Thread priority: " + Thread.currentThread().getPriority()); } }; EventQueue.invokeLater(runner); } } If you run Threads, you'll see that the main thread has a priority of 5, and the event thread runs at a priority 6. >> java Threads Main Thread priority: 5 Event Thread priority: 6 The higher priority for the event thread is desirable. You want your user interfaces to be responsive. But, you don't want to extend that higher priority to non-event processing tasks. So be sure to lower the priority of user-created threads initialized from the event dispatch thread. This means: Change: Thread t = new Thread(longTask); t.start(); To: Thread t = new Thread(longTask); t.setPriority(Thread.NORM_PRIORITY); t.start(); Threads created with this new priority will not compete for processing time with the event dispatch thread. If there is something to run on the event dispatch thread, it will win -- not the worker thread. You might consider creating a WorkerThread class for just this purpose. That way you won't have to keep calling setPriority() for all new threads created from the event dispatch thread, or use a thread pool through the following new classes in the java.util.concurrent package: o Executors.newCachedThreadPool() For a thread pool with unbound size o Executors.newFixedThreadPool(int size) For a thread pool of fixed size > 1 o Executors.newSingleThreadExecutor() For a thread pool of fixed size = 1 Executor was added to the standard libraries with JDK 5.0. Use SwingUtilities For Running Tasks on the Event Dispatch Thread This isn't really an urban legend, but rather an explanation of the use of the EventQueue class in the first legend. Many people are familiar with the SwingUtilities class for the use of invokeLater() and invokeAndWait(). Where did this EventQueue class come from? The answer is that all these methods in SwingUtilities wrap calls to the same methods of the EventQueue class in the java.awt package. In other words, there is a level of indirection of the method calls when used through SwingUtilities. There is technically nothing wrong with using the methods. It's just that you can avoid the indirection by using the EventQueue methods directly. Note that another method that wraps access to the EventQueue class is isEventDispatchThread(). This is used to check if the current task is running on the event dispatch thread. If the SwingUtilities methods are just wrapper methods, why do they exist? When the Swing components became available with JDK 1.2, Sun released a version that worked with JDK 1.1. All the Swing bits needed to be self-contained. In other words, the Swing classes couldn't use anything that was introduced to JDK 1.2. For that reason, SwingUtilities contains the methods for invokeLater and invokeAndWait. Since those methods simply pass along the method calls to EventQueue, you should call the EventQueue methods directly. Synchronize Methods for Synchronization Another commonly-seen practice that hinders performance involves the use of the synchronized keyword. It is common to synchronize methods to prevent simultaneous execution. In many cases, this approach is fine. When might having synchronized methods be bad and slow down performance? When the class is a subclass of an AWT or Swing component, specifically any subclass of java.awt.Component. What's wrong with synchronizing methods in subclasses of Component? If you are only trying to synchronize access to your methods, having a synchronized method means you are competing with all the other synchronized methods of Component. This causes your method to be blocked when it shouldn't, and other Component methods to be blocked when they shouldn't. In fact, this could also lead to unexpected deadlocks. Instead of synchronizing at the method level, you can create a lock variable that is shared by the methods that need to be synchronized. Here's an example: public class Foo extends JComponent { private final Object lock = new Object(); private final char[] chars; public void setMethod(String value) { synchronized(lock) { // save off value as chars chars = value.toCharArray(); } } public String getMethod() { synchronized(lock) { // regenerate saved value from chars String savedValue = new String(chars); return savedValue; } } } For some more insights into Swing performance, see the transcript of the SDN chat, Getting High Performance from Your Desktop Client (http://java.sun.com/developer/community/chat/JavaLive/2005/jl0215.html). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FROM RUNTIME.EXEC() TO PROCESSBUILDER Before JDK 5.0, the only way to fork off a process and execute it local to the user runtime was to use the exec() method of the java.lang.Runtime class. JDK 5.0 adds a new way of executing a command in a separate process, through a class called ProcessBuilder. You can find ProcessBuilder in the java.lang package (like Runtime and Process). This tip discusses and compares both approaches. If you're familiar with the Runtime class, you know that it also allows you to discover memory usage and add a shutdown hook. But probably the most popular use of the class prior to 5.0 was to execute a command in a separate process. This was done through one of the six versions of the exec() method of Runtime: public Process exec(String command) throws IOException public Process exec(String command, String[] envp) throws IOException public Process exec(String command, String[] envp, File dir) throws IOException public Process exec(String[] cmdarray) throws IOExceptionjava public Process exec(String[] cmdarray, String[] envp) throws IOException public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException Before you call the exec() method, you specify the command and its arguments, environment variable settings, and working directory. All versions of the method return a java.lang.Process object for managing the created process. This allows you to get the input or output stream of the subprocess and exit status (among other available information). Here's an example, DoRuntime, that shows how to execute a command with the original Runtime class. The command to run is passed in from the command line. import java.io.*; import java.util.*; public class DoRuntime { public static void main(String args[]) throws IOException { if (args.length <= 0) { System.err.println("Need command to run"); System.exit(-1); } Runtime runtime = Runtime.getRuntime(); Process process = runtime.exec(args); InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; System.out.printf("Output of running %s is:", Arrays.toString(args)); while ((line = br.readLine()) != null) { System.out.println(line); } } } If you run DoRuntime in Solaris like this: java DoRuntime ls You get output that looks something like this (which depends on the contents of the directory): Output of running ls is:DoRuntime.class DoRuntime.java Linux users could also pass in "ls" as the command to get a directory listing. On a Microsoft Windows platform, commands such as "dir" are internal to the command processor so the single command-line argument would be the quoted string: "cmd /c dir" (again, output would depend on the contents of the directory). > java DoRuntime "cmd /c dir" Output of running cmd /c dir is: ... Directory of C:\... 07/15/2005 09:30 AM