BBS水木清华站∶精华区

发信人: abcd12 (Forgiven Not Forgoten), 信区: Java        
标  题: java thread programming(2) 
发信站: BBS 水木清华站 (Sun Aug 13 22:53:46 2000) 
 
This chapter explores more of the Thread API, including getting a handle on 
 the currently executing thread, thread naming, some of the different constr 
uctors, and putting a thread to sleep for a while. You will use these featur 
es to enhance the TwoThread example from Chapter 2, "A Simple Two-Thread Exa 
mple." 
Using Thread.currentThread() 
At times, it's useful to know which of the threads currently running in the  
Java Virtual Machine (JavaVM) is executing a segment of code. In multi-threa 
ded programming, more than one thread may enter a method and execute the sta 
tements within it. 
In the TwoThread example from Chapter 2, two threads execute the code in the 
 println() method of the PrintStream object referred to by the System.out re 
ference: 
System.out.println("Main thread"); 
System.out.println("New thread"); 
This one PrintStream object is set up by the JavaVM during initialization fo 
r use by any class at any time in a Java program through the System.out refe 
rence. Any running thread can invoke the println() method. In this case, the 
 println() method does not care which thread invoked it; it simply takes the 
 String passed in and prints it. 
In the Thread API, the static method gets a reference to the Thread object o 
f the particular thread that is executing a segment of code, as follows: 
public static native Thread currentThread() 
Depending on which thread is currently executing the statements in a method, 
 that method may perform different actions. 
---------------------------------------------------------------------------- 
---- 
NOTE: Many of the methods in Thread are listed with some of the following mo 
difiers: native, final, static, and synchronized. As a quick review, native  
methods are implemented in non-Java code (typically C or C++ in the JDK). Me 
thods declared to be final may not be overridden in a subclass. When a metho 
d is static, it does not pertain to a particular instance of the class, but  
operates at the class level. The synchronized modifier guarantees that no mo 
re than one thread is allowed to execute the statements inside a method at o 
ne time. Later in this book, the synchronized modifier is explained in detai 
l. 
---------------------------------------------------------------------------- 
---- 
As an example, look at a new version of TwoThread in Listing 3.1. This examp 
le is a bit ridiculous for real-world applications, but serves to illustrate 
 a use of Thread.currentThread(). 
Listing 3.1 TwoThead.java--A Version of TwoThread That Uses currentThread() 
View Code 
In this version, the System.out.println() statements have been removed from  
the loops and replaced by a call to the new printMsg() method (lines 11 and  
33). This method does not take a String as a parameter, but instead determin 
es which message to print, based on the thread that invokes it. 
In the constructor (line 6), Thread.currentThread() is used to gain a refere 
nce to the Thread object for the thread that executed the new TwoThread(): s 
tatement. 
This Thread reference is stored in the member variable creatorThread for lat 
er use. 
To determine which message to print, printMsg first gets a reference to the  
Thread that invoked it, by using the static method Thread.currentThread() (l 
ine 17). Next, it tests whether this reference t matches the creatorThread r 
eference stored by the constructor. If so, it prints Creator thread (lines 1 
9 and 20). If the reference doesn't match, printMsg then checks whether the  
t matches this. TwoThread IS-A Thread because it directly subclasses it. The 
 this reference refers to the Thread object constructed on line 29 by the ma 
in thread. If the current thread equals this, New thread is printed (lines 2 
1 and 22). Otherwise, another thread that was not accounted for invoked this 
 method, and the message Mystery thread --unexpected! is printed (lines 23 a 
nd 24). In this example, the Mystery thread --unexpected! message will never 
 be printed because only two threads will run this code and they have both b 
een accounted for. 
Listing 3.2 presents possible output from running TwoThread. Remember that t 
he exact order of the messages printed, as well as how long each thread will 
 run between context switches, depends on the thread scheduler. Therefore, y 
our output might differ somewhat. 
Listing 3.2 Possible Output from TwoThread Using currentThread() 
Creator thread 
New thread 
Creator thread 
New thread 
Creator thread 
New thread 
Creator thread 
New thread 
Creator thread 
New thread 
Creator thread 
New thread 
Creator thread 
New thread 
Creator thread 
New thread 
Creator thread 
New thread 
Creator thread 
New thread 
Naming a Thread: getName() and setName() 
Every Thread has a name associated with it. If a name is not explicitly supp 
lied, a default one is generated during construction. By name, you can diffe 
rentiate between the various threads running in the JavaVM. 
Using getName() 
In the Thread API, the method 
public final String getName() 
is used to retrieve the current name. Listing 3.3 shows a new class, TwoThre 
adGetName, that uses the getName() method to differentiate between two runni 
ng threads. 
Listing 3.3 TwoThreadGetName.java--Using getName() 
 1: public class TwoThreadGetName extends Thread { 
 2:     public void run() { 
 3:         for ( int i = 0; i < 10; i++ ) { 
 4:             printMsg(); 
 5:         } 
 6:     } 
 7: 
 8:     public void printMsg() { 
 9:         // get a reference to the thread running this 
10:         Thread t = Thread.currentThread(); 
11:         String name = t.getName(); 
12:         System.out.println("name=" + name); 
13:     } 
14: 
15:     public static void main(String[] args) { 
16:         TwoThreadGetName tt = new TwoThreadGetName(); 
17:         tt.start(); 
18: 
19:         for ( int i = 0; i < 10; i++ ) { 
20:             tt.printMsg(); 
21:         } 
22:     } 
23: } 
All printing occurs in printMsg() when it is invoked from the loop in run()  
(lines 3-5) and from the loop in main() (lines 19-21). First, in printMsg(), 
 a reference to the currently executing Thread is obtained using Thread.curr 
entThread() (line 10). Next, the name of this particular Thread is retrieved 
 through getName() (line 11). Finally, the name is printed (line 12). 
Listing 3.4 shows possible output from running TwoThreadGetName. Different o 
utput can (and usually does) occur each time this is run. For this particula 
r run, note that the messages alternate between threads at first. However, a 
bout midway, three Thread-0 messages are printed consecutively without any m 
essages from the other thread. At the end, the Thread-0 thread has died, and 
 the main thread is able to catch up and print the backlogged messages. 
This is a perfect example of the nondeterministic behavior of multi-threaded 
 programs--a critical issue for skilled Java developers to be aware of. In l 
ater chapters, you will learn techniques for ensuring the correctness of mul 
ti-threaded programs--regardless of the order in which the threads happen to 
 be scheduled to run. 
Listing 3.4 Possible Output from TwoThreadGetName 
name=main 
name=Thread-0 
name=main 
name=Thread-0 
name=main 
name=Thread-0 
name=main 
name=Thread-0 
name=main 
name=Thread-0 
name=Thread-0 
name=Thread-0 
name=main 
name=Thread-0 
name=Thread-0 
name=main 
name=Thread-0 
name=main 
name=main 
name=main 
In addition to a thread named main, the JavaVM starts up other threads autom 
atically. Table 3.1 lists the names of these threads on each of the three re 
leased Java platforms. Each row presents the various names of the same threa 
d. 
Table 3.1 Threads Started by the JavaVM 
JDK 1.2 JDK 1.1 JDK 1.0 
main main main 
Finalizer Finalizer Finalizer 
Reference Handler (none) (none) 
Signal dispatcher (none) (none) 
AWT-Windows AWT-Windows AWT-Win32 
AWT-EventQueue-0 AWT-EventQueue-0 AWT-Callback-Win32 
SunToolkit.PostEventQueue-0 (none) (none) 
Screen Updater Screen Updater Screen Updater 
Note that the Reference Handler , Signal dispatcher , and SunToolkit.PostEve 
ntQueue-0 threads are new to JDK 1.2. The threads named main and Finalizer ( 
 Reference Handler and Signal dispatcher for JDK 1.2) are started automatica 
lly for every application. The remaining threads are also started by the Jav 
aVM when the application contains any graphical components from AWT or Swing 
. Therefore, in a JDK 1.2 application with a graphical user interface (GUI), 
 eight threads are automatically started by the JavaVM. 
As mentioned previously, the main thread is responsible for starting applica 
tion execution by invoking the main() method. This is the thread from which  
most other developer-defined threads are spawned by application code. The Fi 
nalizer thread is used by the JavaVM to execute the finalize() method of obj 
ects just before they are garbage collected. The AWT-EventQueue-0 thread is  
more commonly known as the event thread and invokes event-handling methods s 
uch as actionPerformed(), keyPressed(), mouseClicked(), and windowClosing(). 
 
Using setName() 
In the preceding example, the names associated with threads are their defaul 
t names. You explicitly specify the name of a Thread object by using the set 
Name() method: 
public final void setName(String newName) 
The name of a thread is typically set before the thread is started, but sett 
ing the name of a thread already running is also permitted. Two Thread objec 
ts are permitted to have the same name, but you should avoid this for clarit 
y. The main thread started by the JavaVM can also have its name changed, but 
 this is also discouraged. 
---------------------------------------------------------------------------- 
---- 
TIP: Although Java requires none of the following, it's good practice to fol 
low these conventions when naming threads: 
---------------------------------------------------------------------------- 
---- 
Invoke setName() on the Thread before start(), and do not rename the Thread  
after it is started. 
Give each thread a brief, meaningful name when possible. 
Give each thread a unique name. 
Do not change the names of JavaVM threads, such as main. 
Listing 3.5 shows a new class, TwoThreadSetName, that uses the setName() met 
hod to override the default thread name. 
Listing 3.5 TwoThreadSetName.java--Using setName() 
 1: public class TwoThreadSetName extends Thread { 
 2:     public void run() { 
 3:         for ( int i = 0; i < 10; i++ ) { 
 4:             printMsg(); 
 5:         } 
 6:     } 
 7: 
 8:     public void printMsg() { 
 9:         // get a reference to the thread running this 
10:         Thread t = Thread.currentThread(); 
11:         String name = t.getName(); 
12:         System.out.println("name=" + name); 
13:     } 
14: 
15:     public static void main(String[] args) { 
16:         TwoThreadSetName tt = new TwoThreadSetName(); 
17:         tt.setName("my worker thread"); 
18:         tt.start(); 
19: 
20:         for ( int i = 0; i < 10; i++ ) { 
21:             tt.printMsg(); 
22:         } 
23:     } 
24: } 
The only difference between TwoThreadSetName and TwoThreadGetName is the add 
ition of the statement to set the name of the new thread to my worker thread 
 (line 17) before the thread is started. The name of the main thread remains 
 untouched. Listing 3.6 shows possible output; your output might differ beca 
use of nondeterministic thread scheduling. 
Listing 3.6 Possible Output from TwoThreadSetName 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=main 
Thread Constructors 
The central constructor in Thread is 
public Thread(ThreadGroup group, Runnable target, String name) 
The parameter name allows you to specify the name of the thread at construct 
ion time, rather than set it later using setName(). 
The parameter target refers to an object of type Runnable. This object's run 
() method should be invoked by the new thread instead of this Thread. Unlike 
 a thread's name, if you're going to specify the target, you must do so at t 
he time of construction. Chapter 4 explores in detail the issues involved in 
 making a decision to use Runnable instead of extending Threads. 
The parameter group lets you specify the ThreadGroup to which the new Thread 
 will belong. Figure 3.1 shows the relationships among Threads and ThreadGro 
ups. 
FIGURE 3.1 A sample hierarchy of ThreadGroups and Threads. 
An analogy can be drawn between the file system concept of directories and f 
iles and the ThreadGroup/Thread relationship. In a file system, a directory  
can contain files and other directories. These other directories, in turn, c 
an contain other files and directories. Every file is in exactly one directo 
ry, which may itself be in another directory. Every directory except the one 
 "root" or "base" directory is in another directory. 
In Java, a ThreadGroup (much like a directory) can contain Threads and other 
 ThreadGroups. These other ThreadGroups can, in turn, contain other Threads  
and ThreadGroups. Every Thread (much like a file) is a member of a ThreadGro 
up, which may itself be a member of another ThreadGroup. 
If a ThreadGroup is not specified at the time of a Thread's construction, th 
e ThreadGroup is inherited from the Thread that constructed it. Chapter 10,  
"The ThreadGroup API," discusses ThreadGroups in more detail. 
The Thread constructor used so far has been as follows: 
public Thread() 
By default, the ThreadGroup is that of the Thread that constructs it. No ext 
ernal Runnable is specified, so the Thread's own run() method is called. Bec 
ause no name is specified, the name of the Thread will be automatically gene 
rated as something such as Thread-0. 
The other constructors of Thread come somewhere in between the zero-argument 
 constructor and the three-argument constructor. They specify some parameter 
s of the three-argument constructor, and the nonspecified parameters take on 
 their default values. 
Enlivening a Thread: start() and isAlive() 
The start() method signals the thread scheduler that this new thread is read 
y to go and should have its run() method invoked at the scheduler's earliest 
 convenience. Because of the nondeterministic nature of multi-threaded progr 
amming, one can not be sure just when the thread will begin execution, but o 
nly that it should begin soon. 
The API for start() is 
public native synchronized void start() throws IllegalThreadStateException 
If the Thread has already been started, an IllegalThreadStateException will  
be thrown. When the start() method of a Thread is invoked, the new thread is 
 considered to come alive. The thread remains alive until the run() method r 
eturns or until the thread is abruptly stopped by the stop() method (which i 
s a deprecated method, as of JDK 1.2!). 
The method 
public final native boolean isAlive() 
can be used on Thread to test whether a thread has been started and is still 
 running. Listing 3.7 shows an example of how isAlive() can be used. 
Listing 3.7 TwoThreadAlive.java--Using isAlive() 
View Code 
The code on line 19 checks whether the new Thread object is alive. This will 
 always be false because the thread has not yet been started. Immediately af 
ter the thread is started, another check is done (line 20). At this point, t 
he new Thread object will always be alive. At the end of main(), one last ch 
eck is done (lines 27 and 28). Sometimes, the Thread object will still be al 
ive, and other times it will have already died, depending on the exact threa 
d scheduling that occurs. 
Listing 3.8 presents output from a particular run of TwoThreadAlive. Nearly  
every time this is run, it gives slightly different output. In this case, no 
te that three messages are printed from the worker thread before the main th 
read has a chance to execute the isAlive() check right after start(). The wo 
rker thread keeps its lead and finishes its work--and is therefore no longer 
 alive--well before the main is done, so the check at the end shows that the 
 worker thread is no longer alive. 
Listing 3.8 Possible Output from TwoThreadAlive 
before start(), tt.isAlive()=false 
name=my worker thread 
name=my worker thread 
name=my worker thread 
just after start(), tt.isAlive()=true 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=main 
name=main 
name=main 
name=main 
at the end of main(), tt.isAlive()=false 
Using Thread.sleep() 
As beneficial as it is for threads to go about doing their work as fast as p 
ossible, sometimes it would be useful if a thread could take a break and go  
to sleep for a while. In a clock application, it might be the case that the  
thread in charge of updating the displayed time should pause for 60 seconds  
at a time between the changing of the minutes displayed. A busy loop such as 
 
long startTime = System.currentTimeMillis(); 
long stopTime = startTime + 60000; 
while ( System.currentTimeMillis() < stopTime ) { 
    // do nothing, but loop back 

takes up a lot of processor cycles. Instead, it would be better to use the f 
ollowing static method on Thread 
View Code 
to wait for 60 seconds, like this: 
try { 
    Thread.sleep(60000); 
} catch ( InterruptedException x ) { 
    // ignore the exception 

Sleeping is a much better option than using a busy loop. A sleeping thread d 
oes not use any processor cycles because its execution is suspended for the  
specified duration. 
The sleep() method is static and puts only the currently executing thread--t 
he one that would be returned by Thread.currentThread()--to sleep. It is not 
 possible for a thread to put any other thread to sleep. 
The try/catch construct is necessary because while a thread is sleeping, it  
might be interrupted by another thread. One thread might want to interrupt a 
nother to let it know that it should take some sort of action. Later chapter 
s further explore the use of interrupts. Here, it suffices to say that a sle 
eping thread might be interrupted and will throw an InterruptedException if  
this occurs. 
Listing 3.9 shows how sleep() can be used to slow down the action and how tw 
o threads may be inside the same method of one object at the same time. 
Listing 3.9 TwoThreadSleep.java--Using sleep() 
View Code 
The method loop() is used by both run() (line 3) and by main() (line 38) to  
print out all the messages. In main(), sleep() is used to delay the main thr 
ead's entry into the loop() method (lines 32-36). On lines 14-18, sleep() is 
 also used to slow down the iterations through the for loop. Listing 3.10 sh 
ows sample output from a particular run. 
Listing 3.10 Sample Output from TwoThreadSleep 
just entered loop() - my worker thread 
name=my worker thread 
name=my worker thread 
just entered loop() - main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
name=main 
name=my worker thread 
about to leave loop() - my worker thread 
name=main 
name=main 
name=main 
about to leave loop() - main 
In examining the output, you notice that both threads are inside the loop()  
method at the same time . Yet, each thread has its own copy of the local var 
iable name to print its proper identification. Local variables work well wit 
h multiple threads, but accessing and modifying member variables (the state  
of an object) with multiple threads is tricky business. You will learn more  
about this in Chapter 7, "Controlling Concurrent Access to an Object." 
Summary 
This chapter begins to explore some of the API for Thread: 
Thread.currentThread() 
getName() 
setName() 
Several constructors 
start() 
isAlive() 
Thread.sleep() 
The next chapters explain more of the API. 
 
-- 
 
   WE DON'T KNOW WHO WE ARE UNTIL WE SEE WHAT WE DO! 
 
 
※ 来源:·BBS 水木清华站 smth.org·[FROM: 202.204.9.81] 

BBS水木清华站∶精华区