Basic threads
The simplest way to create a thread is to inherit from java.lang.Thread, which has all the wiring necessary to create and run threads. The most important method for Thread is run( ), which you must override to make the thread do your bidding. Thus, run( ) is the code that will be executed “simultaneously” with the other threads in a program.
The following example creates five threads, each with a unique identification number generated with a static variable. The Thread’s run( ) method is overridden to count down each time it passes through its loop and to return when the count is zero (at the point when run( ) returns, the thread is terminated by the threading mechanism).
//: c13:SimpleThread.java
// Very simple Threading example.
import com.bruceeckel.simpletest.*;
public class SimpleThread extends Thread {
private static Test monitor = new Test();
private int countDown = 5;
private static int threadCount = 0;
public SimpleThread() {
super("" + ++threadCount); // Store the thread name
start();
}
public String toString() {
return "#" + getName() + ": " + countDown;
}
public void run() {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
}
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++)
new SimpleThread();
monitor.expect(new String[] {
"#1: 5",
"#2: 5",
"#3: 5",
"#5: 5",
"#1: 4",
"#4: 5",
"#2: 4",
"#3: 4",
"#5: 4",
"#1: 3",
"#4: 4",
"#2: 3",
"#3: 3",
"#5: 3",
"#1: 2",
"#4: 3",
"#2: 2",
"#3: 2",
"#5: 2",
"#1: 1",
"#4: 2",
"#2: 1",
"#3: 1",
"#5: 1",
"#4: 1"
}, Test.IGNORE_ORDER + Test.WAIT);
}
} ///:~
The thread objects are given specific names by calling the appropriate Thread constructor. This name is retrieved in toString( ) using getName( ).
A Thread object’s run( ) method virtually always has some kind of loop that continues until the thread is no longer necessary, so you must establish the condition on which to break out of this loop (or, as in the preceding program, simply return from run( )). Often, run( ) is cast in the form of an infinite loop, which means that, barring some factor that causes run( ) to terminate, it will continue forever (later in the chapter you’ll see how to safely signal a thread to stop).
In main( ) you can see a number of threads being created and run. The start( ) method in the Thread class performs special initialization for the thread and then calls run( ). So the steps are: the constructor is called to build the object, it calls start( ) to configure the thread, and the thread execution mechanism calls run( ). If you don’t call start( ) (which you don’t have to do in the constructor, as you will see in subsequent examples), the thread will never be started.
The output for one run of this program will be different from that of another, because the thread scheduling mechanism is not deterministic. In fact, you may see dramatic differences in the output of this simple program between one version of the JDK and the next. For example, a previous JDK didn’t time-slice very often, so thread 1 might loop to extinction first, then thread 2 would go through all of its loops, etc. This was virtually the same as calling a routine that would do all the loops at once, except that starting up all those threads is more expensive. In JDK 1.4 you get something like the output from SimpleThread.java, which indicates better time-slicing behavior by the scheduler—each thread seems to be getting regular service. Generally, these kinds of JDK behavioral changes have not been mentioned by Sun, so you cannot plan on any consistent threading behavior. The best approach is to be as conservative as possible while writing threading code.
When main( ) creates the Thread objects, it isn’t capturing the references for any of them. With an ordinary object, this would make it fair game for garbage collection, but not with a Thread. Each Thread “registers” itself so there is actually a reference to it someplace, and the garbage collector can’t clean it up until the thread exits its run( ) and dies.