A thread's life
There are several ways to create a thread in a Java program.
Every Java program contains at least one thread: the main
thread. Additional threads are created through the
Thread constructor or by instantiating classes
that extend the Thread class.
Java threads can create other threads by instantiating a
Thread object directly or an object that
extends Thread.
In the example in Thread basics
,
in which we calculated as many primes as we could in ten seconds, we created a
thread by instantiating an object of type
CalculatePrimes,
which extends
Thread.
When we talk about threads in Java programs, there are two
related entities we may be referring to: the actual thread
that is doing the work or the Thread object
that represents the thread. The running thread is
generally created by the operating system; the
Thread object is created by the Java VM as a
means of controlling the associated thread.
Creating threads and starting threads are not the same
A thread doesn't actually begin to execute until another
thread calls the start() method on the
Thread object for the new thread. The
Thread object exists before its thread actually
starts, and it continues to exist after its thread exits. This
allows you to control or obtain information about a thread
you've created, even if the thread hasn't started yet or has
already completed.
It's generally a bad idea to start() threads
from within a constructor. Doing so could expose partially
constructed objects to the new thread. If an object owns a
thread, then it should provide a start() or
init() method that will start the thread,
rather than starting it from the constructor. (See Resources for
links to articles that provide a more detailed explanation of this concept.)
A thread will end in one of three ways:
- The thread comes to the end of its
run()method.
- The thread throws an
ExceptionorErrorthat is not caught.
- Another thread calls one of the deprecated
stop()methods. Deprecated means they still exist, but you shouldn't use them in new code and should strive to eliminate them in existing code.
When all the threads within a Java program complete, the program exits.
The Thread API contains a method for waiting for another
thread to complete: the join() method. When
you call Thread.join(),
the calling thread will
block until the target thread completes.
Thread.join() is generally used by programs
that use threads to partition large problems into
smaller ones, giving each thread a piece of the problem.
The example at the end of this section creates ten threads,
starts them, then uses Thread.join() to wait
for them all to complete.
Except when using Thread.join() and
Object.wait(),
the timing of thread scheduling
and execution is nondeterministic. If two threads are
running at the same time and neither is waiting, you must
assume that between any two instructions, other threads may
be running and modifying program variables. If your thread
will be accessing data that may be visible to other
threads, such as data referenced directly or indirectly from
static fields (global variables), you must use
synchronization to ensure data consistency.
In the simple example below, we'll create and start two
threads, each of which prints two lines to System.out:
public class TwoThreads {
public static class Thread1 extends Thread {
public void run() {
System.out.println("A");
System.out.println("B");
}
}
public static class Thread2 extends Thread {
public void run() {
System.out.println("1");
System.out.println("2");
}
}
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
}
}
|
We have no idea in what order the lines will execute, except that "1" will be printed before "2" and "A" before "B." The output could be any one of the following:
- 1 2 A B
- 1 A 2 B
- 1 A B 2
- A 1 2 B
- A 1 B 2
- A B 1 2
Not only may the results vary from machine to machine, but running the same program multiple times on the same machine may produce different results. Never assume one thread will do something before another thread does, unless you've used synchronization to force a specific ordering of execution.
The Thread API includes a sleep() method, which
will cause the current thread to go into a wait state until
the specified amount of time has elapsed or until the
thread is interrupted by another thread calling
Thread.interrupt() on the current thread's
Thread object. When the specified time
elapses, the thread again becomes runnable and goes back
onto the scheduler's queue of runnable threads.
If a thread is interrupted by a call to
Thread.interrupt(),
the sleeping thread will
throw an InterruptedException so that the
thread will know that it was awakened by an interrupt and
won't have to check to see if the timer expired.
The Thread.yield() method is like
Thread.sleep(),
but instead of sleeping, it simply
pauses the current thread momentarily so that other threads
can run. In most implementations, threads with lower
priority will not run when a thread of higher priority
calls Thread.yield().
The CalculatePrimes example used a background
thread to calculate primes, then slept for ten seconds.
When the timer expired, it set a flag to indicate that the
ten seconds had expired.
We mentioned that a Java program exits when all of its threads have completed, but this is not exactly correct. What about the hidden system threads, such as the garbage collection thread and others created by the JVM? We have no way of stopping these. If those threads are running, how does any Java program ever exit?
These system threads are called daemon threads. A Java program actually exits when all its non-daemon threads have completed.
Any thread can become a daemon thread. You can indicate a
thread is a daemon thread by calling the
Thread.setDaemon() method. You might want to
use daemon threads for background threads that you create in
your programs, such as timer threads or other deferred event
threads, which are only useful while there are other
non-daemon threads running.
Example: Partitioning a large task with multiple threads
In this example, TenThreads shows a program that
creates ten threads, each of which do some work. It
waits for them all to finish, then gathers the results.
/**
* Creates ten threads to search for the maximum value of a large matrix.
* Each thread searches one portion of the matrix.
*/
public class TenThreads {
private static class WorkerThread extends Thread {
int max = Integer.MIN_VALUE;
int[] ourArray;
public WorkerThread(int[] ourArray) {
this.ourArray = ourArray;
}
// Find the maximum value in our particular piece of the array
public void run() {
for (int i = 0; i < ourArray.length; i++)
max = Math.max(max, ourArray[i]);
}
public int getMax() {
return max;
}
}
public static void main(String[] args) {
WorkerThread[] threads = new WorkerThread[10];
int[][] bigMatrix = getBigHairyMatrix();
int max = Integer.MIN_VALUE;
// Give each thread a slice of the matrix to work with
for (int i=0; i < 10; i++) {
threads[i] = new WorkerThread(bigMatrix[i]);
threads[i].start();
}
// Wait for each thread to finish
try {
for (int i=0; i < 10; i++) {
threads[i].join();
max = Math.max(max, threads[i].getMax());
}
}
catch (InterruptedException e) {
// fall through
}
System.out.println("Maximum value was " + max);
}
}
|
Like programs, threads have a life cycle: they start, they execute, and they complete. One program, or process, may contain multiple threads, which appear to execute independently of each other.
A thread is created by instantiating a Thread
object, or an object that extends Thread,
but
the thread doesn't start to execute until the
start() method is called on the new
Thread object. Threads end when they come to the
end of their run() method or throw an unhandled
exception.
The sleep() method can be used to wait for a
certain amount of time; the join() method can
be used to wait until another thread completes.

