|
Hello Gerard,
Thanks for the detailed reference. Unforunately, the
recommended solution (below) doesn't help at all.
Thread t = new
Thread(longTask); t.setPriority(Thread.NORM_PRIORITY); t.start();
It looks like I'll have to post a bug with Apple.
Charles.
----- Original Message -----
Sent: Wednesday, January 11, 2006 8:59
AM
Subject: Re: SwingUtilities.invokeLater
hang or deadlock
In a message dated 1/11/2006 8:08:41 AM Pacific Standard Time, email@hidden writes:
It is not clear to me how you should do that with invokeLater but
maybe the listed documentation makes that clear. Given that it can be done,
setting the invokeLater thread to NORM_PRIORITY I would think you still have
a potential for deadlock with the same priority AWT-AppKit thread, even if
you no longer do with the 6 priority EventQueue-0
thread.
Check this documentation for an
explanation regarding setting the priority of the threads and see the example
at the bottom.
regards, gerard
| You are receiving this e-mail because you elected to receive
e-mail from Sun Microsystems, Inc. To update your communications
preferences, please see the link at the bottom of this message. We
respect your privacy and post our privacy policy prominently on our Web
site http://sun.com/privacy/ |
![]() |
In this Issue |
![]() |
|
| |
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 the Java 2 Platform Standard
Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://enews.sun.com/CTServlet?id=89724473-1293635476:1122503962347.
This issue of the Core Java Technologies Tech Tips is written by
John Zukowski, president of JZ Ventures, Inc.
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 where enthusiasts of Java technology can collaborate and
build 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:
- Create threads for long tasks from the event dispatch thread.
- Use
SwingUtilities for running tasks on the event
dispatch thread.
- 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:
Executors.newCachedThreadPool() For a thread pool
with unbound size
Executors.newFixedThreadPool(int size) For a
thread pool of fixed size > 1
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.
Back
to Top |
|
|
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 <DIR> .
07/15/2005 09:30 AM <DIR> ..
07/15/2005 09:30 AM 1,146 DoRuntime.class
07/15/2005 09:23 AM 724 DoRuntime.java
...
As coded, the command executes in the current working directory
with its environment variables intact.
If you want to run the
command in a different directory, and you need to add more arguments to
the exec() command, you change: Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
to: File file = new File(other directory);
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command, null, file);
The second parameter in the call to the exec() method
identifies the environment variable settings. Because the parameter is
"null", the subprocess inherits the environment settings of the current
process.
So what's wrong with this approach? Why create a new
approach? The problem is that the Runtime.exec approach
doesn't necessarily make it easy to customize and invoke subprocesses.
The new ProcessBuilder class simplifies things. Through
various methods in the class, you can easily modify the environment
variables for a process and start the process.
Here's a simple
use of ProcessBuilder that duplicates the functions of the
DoRuntime example: import java.io.*;
import java.util.*;
public class DoProcessBuilder {
public static void main(String args[]) throws IOException {
if (args.length <= 0) {
System.err.println("Need command to run");
System.exit(-1);
}
Process process = new ProcessBuilder(args).start();
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);
}
}
}
> java DoProcessBuilder ls
Output of running ls is:DoProcessBuilder.class
DoProcessBuilder.java
DoRuntime.class
DoRuntime.java
Notice that the following two lines in DoRuntime: Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
were changed to the following line in
DoProcessBuilder: Process process = new ProcessBuilder(command).start();
The ProcessBuilder class has two constructors. One
constructor accepts a List for the command and its
arguments. The other constructor accepts a variable number of
String arguments. public ProcessBuilder(List<String> command)
public ProcessBuilder(String... command)
With ProcessBuilder, you call start() to
execute the command. Prior to calling start(), you can
manipulate how the Process will be created. If you want the
process to start in a different directory, you don't pass a
File in as a command line argument. Instead, you set the
process builder's working directory by passing the File to
the directory() method: public ProcessBuilder directory(File directory)
There isn't an obvious setter type method in
ProcessBuilder for setting environment variables. Instead,
you get a Map of the variables through the
environment() method, then you manipulate the
Map: ProcessBuilder processBuilder = new ProcessBuilder(command);
Map<String, String> env = processBuilder.environment();
// manipulate env
The options for manipulating the environment include adding
environment variables with the put() method, and removing
them with the remove() method. For example: ProcessBuilder processBuilder = new ProcessBuilder(
command, arg1, arg2);
Map<String, String> env = processBuilder.environment();
env.put("var1", "value");
env.remove("var3");
After the environment variables and directory are set, call
start(): processBuilder.directory("Dir");
Process p = processBuilder.start();
You can also clear() all the variables from the
environment and explicitly set the ones you want.
With methods
such as environment() for adding and removing environment
variables from the process space, and start() for starting
a new process, ProcessBuilder should make it easier to
invoke a subprocess with a modified process environment.
You can
get the initial set of environment variables by calling the
getenv() method of System. Understand that not
all platforms support changing environment variables. If you try to
change an environment variable on a platform that forbids it, the
operation will throw either an
UnsupportedOperationException or an
IllegalArgumentException. Also, when running with a
security manager, you'll need the RuntimePermission for
"getenv.*", otherwise a SecurityException will
be thrown.
Remember not to forget the start() call
after configuring your instance. And, keep using the
Process class to manipulate the streams for the process and
to get its exit status.
A word of caution about the examples in
this tip. It is possible that the examples will deadlock if the
subprocess generates enough output to overflow the system. A more robust
solution requires draining the process stdout and stderr in separate
threads.
For more information about ProcessBuilder, see the class
definition.
Back
to Top |
|
| Rate and Review |
| Tell us what you think of the content of this
page. |
| Excellent Good Fair Poor |
Comments:
|
If you would like a reply to
your comment, please submit your email address: Note: We may not respond to all
submitted comments. |
|
| |
Comments? Send your feedback on the Tech Tips: http://developers.sun.com/contact/feedback.jsp?category=sdn
Subscribe to the following newsletters for the latest
information about technologies and products in other Java platforms:
- 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).
You can subscribe to these and other Java
technology developer newsletters or manage your current newsletter
subscriptions on the Sun
Developer Network Subscriptions page
ARCHIVES: You'll find
the Core Java Technologies Tech Tips archives at: http://enews.sun.com/CTServlet?id=89724473-1293635476:1122503962430
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://developer.java.sun.com/berkeley_license.html
© 2005 Sun Microsystems, Inc. All Rights Reserved. For
information on Sun's trademarks see: http://sun.com/suntrademarks Java, J2EE, J2SE,
J2ME, and all Java-based marks are trademarks or registered trademarks
of Sun Microsystems, Inc. in the United States and other countries.
Sun Microsystems, Inc. 10 Network Circle, MPK10-209 Menlo Park,
CA 94025 US
|
Here's a snippet that I have run successfully. Before this
implementation my UI component was not able to validate and paint without
throwing the NullPointerException. Using the snippet, the UI updated
correctly and the NullPointerException disappeared. I am using a PC
computer running Windows, Eclipse 3.1 and JDK 1.5.
Runnable oParse_Runnable = new Runnable()
{ public
void run()
{ //
your runnable code goes here
// A Runnable to update the
UI
Runnable oUpdateTheParseUI_Runnable = new Runnable()
{
public void run()
{
do_Update_the_UI();
};
};
EventQueue.invokeLater(oUpdateTheParseUI_Runnable);
};
};
Thread oThread = new
Thread(oParse_Runnable);
oThread.setPriority(Thread.NORM_PRIORITY);
oThread.start(); ![]() gerard
|