java.lang.Thread
java.lang.Thread extends java.lang.Object implements java.lang.Runnable
Syntax
public class Thread implements Runnable
Description
A thread is a single sequential flow of control within a process. A single process can have multiple concurrently executing threads. For example, a process may have a thread reading input from the user, while at the same time another thread is updating a database containing the user's account balance, while at the same time a third thread is updating the display with the latest stock quotes. Such a process is called a multithreaded process; the program from which this process executes is called a multithreaded program.
The Thread
class is used to represent a thread, with methods to control the
execution state of a thread.
To create a new thread of execution, you first declare a new class that is a subclass of
Thread
and override the run()
method with code you want executed
in this thread.
You then create an instance of this subclass, followed by a call to theclass myThread extends Thread { public void run() { // do something } }
start()
method (which really is, because of inheritance, Thread.start()
. That method will
execute the run()
method defined by this subclass. You can achieve the same effect by having the class directly implement themyThread m = new myThread(); m.start(); // something else
Runnable
interface. To create a thread to execute thisclass A implements Runnable { public void run () { // do something } }
run()
method, do the following:
Thread Priorities Each thread has a priority that is used by the Java runtime in scheduling threads for execution. A thread that has a higher priority than another thread is typically scheduled ahead of the other thread. However, the way thread priorities are precisely affect scheduling is platform-dependent. A thread inherits its priority from the thread that created it. A thread's priority can be changed subsequent to the thread's creation at any time using theA a = new A(); Thread m = new Thread(a); m.start(); // do something else
setPriority()
method in the Thread
class, if allowed by the security manager.
Thread State and Synchronization between Threads When a thread is started,
its state is active. Its state remains active until it has terminated execution
or is stopped. An active thread can be executing or suspended. When a thread is first
started, it starts executing its run()
method. The Thread
class
provides methods for you to suspend an executing thread, to resume execution of a suspended thread,
and to stop a thread completely (it can no longer run unless restarted at the beginning of its
run()
method). These methods can be invoked only if allowed by the security manager.
In addition to these methods in the Thread
class, you can also use synchronization
methods available in the Object
class (wait()
and notify()
)
to control the execution of a thread.
Interrupts A thread can send an interrupt to another thread. This sets a flag in the target thread to indicate that it has been interrupted. The target thread can then check for this flag at its discretion and react appropriately.
Member Summary
Constructor | |
Thread() | Constructs a new Thread instance. |
Thread Properties Fields and Methods | |
getName() | Retrieves this threads's name. |
getPriority() | Retrieves this threads's priority. |
getThreadGroup() | Retrieves this threads's thread group. |
isDaemon() | Determines if this thread is a daemon thread. |
MAX_PRIORITY | The maximum priority that a thread can have. |
MIN_PRIORITY | The minimum priority that a thread can have. |
NORM_PRIORITY | The default priority that is assigned to the first user thread. |
setDaemon() | Changes this thread's daemon status. |
setName() | Changes this thread's name. |
setPriority() | Changes this thread's priority. |
Thread State Methods | |
destroy() | Destroys this thread without any cleanup. |
isAlive() | Determines if this thread is active. |
join() | Waits for this thread to terminate. |
resume() | Resumes execution of this thread. |
run() | The actual body of this thread. |
sleep() | Causes the currently executing thread to sleep for a period of time. |
start() | Starts execution of this thread. |
stop() | Stops execution of this thread. |
suspend() | Suspends execution of this thread. |
yield() | Causes the currently executing thread object to yield to other threads. |
Interrupt Methods | |
interrupt() | Sends an interrupt to this thread. |
interrupted() | Determines if the currently executing thread has been interrupted. |
isInterrupted() | Determines if this thread has been interrupted. |
Stack Frame Methods | |
countStackFrames() | Counts the number of stack frames in this thread. |
dumpStack() | Prints a snapshot of the current execution stack trace. |
Security Method | |
checkAccess() | Determines if the currently executing thread is allowed to modify this thread. |
Current Thread Methods | |
activeCount() | Estimates the number of active threads in the current thread's threadgroup and its subgroup. |
currentThread() | Retrieves the currently executing thread. |
enumerate() | Enumerates the active threads in the currently executing thread's thread group. |
Description Method | |
toString() | Generates a string representation of the thread. |
See Also
Object
,
Runnable
,
ThreadGroup
,
SecurityManager
Examples
There are four kinds of threads programming:
1. Unrelated Threads
The simplest threads program involves threads of control that do different things and don't interact with each other.
tucotuco.cs.indiana.edu% ls -l total 2 -rw------- 1 dgerman students 661 Nov 12 12:01 drinks.java tucotuco.cs.indiana.edu% cat drinks.java public class drinks { public static void main(String[] a) { Coffee t1 = new Coffee(); t1.start(); new Tea().start(); //an anonymous thread } } class Coffee extends Thread { public void run() { try { while (true) { System.out.println("I like cofee..."); sleep(500); } } catch (InterruptedException e) { return; // end this thread } } } class Tea extends Thread { public void run() { try { while (true) { System.out.println("I like tea..."); sleep(700); } } catch (InterruptedException e) { return; // end this thread } } } tucotuco.cs.indiana.edu% javac drinks.java tucotuco.cs.indiana.edu% ls -l total 8 -rw-r--r-- 1 dgerman students 566 Nov 12 12:04 Coffee.class -rw-r--r-- 1 dgerman students 564 Nov 12 12:04 Tea.class -rw-r--r-- 1 dgerman students 408 Nov 12 12:04 drinks.class -rw------- 1 dgerman students 661 Nov 12 12:01 drinks.java tucotuco.cs.indiana.edu% java drinks I like cofee... I like tea... I like cofee... I like tea... I like cofee... I like tea... I like cofee... I like cofee... I like tea... I like cofee... I like tea... I like cofee... I like tea... I like cofee... I like cofee... I like tea... I like cofee... I like tea... I like cofee... I like cofee... I like tea... I like cofee... I like tea... I like cofee... I like tea... I like cofee... ^Ctucotuco.cs.indiana.edu%This is the easiest to get working.
2. Related but Unsynchronized Types
This level of complexity has threaded code to partition a problem, solving it by having multiple threads work on different pieces of the same data structure. The threads don't interact with each other. Here, threads of control do work that is sent to them, but don't work on shared data, so they don't need to access it in a synchronized way.
An example of this would be spawning a new thread for each socket connection that comes in.
A less common but still interesting example of related but unsynchronized threads involves partitioning a data set, and instantiating multiple copies of the same thread to work on different pieces of the same problem. Be careful not to duplicate work, or even worse, to let two different threads operate on the same data at once.
Here's an example program that tests whether a given number is a prime number. That involves a lot of divisions so it's a good candidate for parcelling the work out among a number of threads. Tell each thread the range of numbers it is to test-divide into the possible prime. Then let them all loose in parallel.
tucotuco.cs.indiana.edu% ls -l total 4 -rw------- 1 dgerman students 661 Nov 12 12:01 drinks.java -rw-r--r-- 1 dgerman students 894 Nov 12 12:35 testPrime.java tucotuco.cs.indiana.edu% cat testPrime.java public class testPrime { public static void main(String s[]) { long possPrime = Long.parseLong(s[0]); int centuries = (int) (possPrime/100) + 1; for (int i=0; i < centuries; i++) { new testRange(i*100, possPrime).start(); } } } class testRange extends Thread { static long possPrime; long from, to; // constructor // record the number we are to test, and // the range of factors we are to try testRange (int argFrom, long argPossPrime) { possPrime = argPossPrime; if (argFrom == 0) from = 2; else from = argFrom; to = argFrom + 99; } public void run () { for (long i = from; i <= to && i < possPrime; i++) { if (possPrime % i == 0) { // i divides possPrime exactly System.out.println("factor " + i + " found by thread " + getName()); this.stop(); } yield(); } } } tucotuco.cs.indiana.edu% javac testPrime.java tucotuco.cs.indiana.edu% java testPrime 7021 factor 7 found by thread Thread-2 factor 119 found by thread Thread-3 factor 413 found by thread Thread-6 factor 1003 found by thread Thread-12 tucotuco.cs.indiana.edu% java testPrime 1001 factor 7 found by thread Thread-2 factor 143 found by thread Thread-3 tucotuco.cs.indiana.edu%3. Mutually-Exclusive Threads
Here's where threads start to interact with each other, and that makes life a little more complicated. In particular we use threads which need to work on the same pieces of the same data structure. These threads need to take steps to stay out of each others' way so that they don't each simultaneously modify the same piece of data leaving an uncertain result. Staying out of each other's way is known as mutual exclusion. Here's an example.
The code below simulates a steam bolier. It defines some values (the current reading of a
pressure gauge, and the safe limit for that gauge), and then instantiates 10 copies of a thread
called pressure
storing them in an array. The main routine concludes by waiting for
each threads to finish (.join
) and then prints the current value of the pressure
gauge.
tucotuco.cs.indiana.edu% ls -l total 6 -rw------- 1 dgerman students 661 Nov 12 12:01 drinks.java -rw-r--r-- 1 dgerman students 830 Nov 12 13:18 steamBoiler.java -rw------- 1 dgerman students 894 Nov 12 12:35 testPrime.java tucotuco.cs.indiana.edu% cat steamBoiler.java public class steamBoiler { static int pressureGauge = 0; static final int safetyLimit = 20; public static void main(String[] args) { pressure[] p1 = new pressure[10]; for (int i = 0; i < 10; i++) { p1[i] = new pressure(); p1[i].start(); } try { for (int i=0; i < 10; i++) p1[i].join(); } catch (Exception e) { } System.out.println("gauge reads " + pressureGauge + ", safelimit is 20."); } } class pressure extends Thread { void RaisePressure () { if (steamBoiler.pressureGauge < steamBoiler.safetyLimit - 15) { // wait briefly to simulate some calculations try { sleep(100); } catch (Exception e) { } steamBoiler.pressureGauge += 15; } else ; // pressure too high -- don't add to it. } public void run() { RaisePressure(); } } tucotuco.cs.indiana.edu% javac steamBoiler.java tucotuco.cs.indiana.edu%Let's run it.
This is a classic example of what is called a data race or a race condition. A race condition occurs when two or more threads update the same value simultaneously.tucotuco.cs.indiana.edu% java steamBoiler gauge reads 150, safelimit is 20. tucotuco.cs.indiana.edu%
To avoid data races, follow this simple rule: whenever two threads access the same data, they must use mutual exclusion. You can optimize slightly, by allowing multiple readers at one instant.
In Java, thread mutual exclusion is built on data Object
s. Every Object
in the
system has its own mutex semaphore (strictly speaking this is only allocated if it is being used), so any
Object
in the system can be used as the "turnstile" or "thread serializer" for threads. You
use the synchronized
keyword and explicitly or implicitly provide an Object
, any
Object
to synchronize on. The runtime system will take over and apply the code to ensure that,
at most, one thread has locked that specific object at any given instant. The synchronized
keyword can be applied to a:
The Java programmer never deals with the low-level and error-prone details of creating, acquiring and releasing locks, but only specifies the region of code and the object that must be exclusively held in that region. You want to make your regions of synchronized code as small as possible, because mutual exclusion really chokes performance. Here are examples of each of these alternatives of synchronizing over a class, a method, or a block, with comments on how exclusion works.
Mutual exclusion over an entire class
This is achieved by aplying the keyword synchronized
to a class method (a method
with the keyword static
). Only one static synchronized
method for a
particular class can be running at any given time. The threads are implicitly synchronized using
the class object.
static synchronized void RaisePressure() {
Mutual exclusion over a block of statements
This is achieved by attaching the keyword synchronized
before a block of code. You
also have to explicitly mention in parens the object whose lock must be acquired before the region
can be entered.
void RaisePressure () { synchronized(Obj) { if (steamBoiler.pressureGauge < steamBoiler.safetyLimit - 15) { // same code as before } else ; // pressure too high -- don't add to it. } }You need to provide the
Obj
object, so we declare it in steamBoiler
:
static Object Obj = new Object();
Mutual exclusion over a method
This is achieved by applying the keyword synchronized
to an ordinary (non-static)
method. Note that in this case the object whose lock will provide the mutual exclusion is implicit,
it is the this
object on which the method is invoked:
synchronized void foo() { ... }is equivalent to
void foo() { synchronized(this) { ... } }Note: this won't work in our example for obvious reasons (each one of the 10 pressure checker threads will be able to seize a lock on themselves and the race condition will reoccur).
Synchronized methods are useful when you have several different methods that might be called simultaneously on the same one object. It ensures that at most one of all the methods designated as synchronized will be invoked on that one object at any given instant.
The synchronized methods will exclude each other but they do not exclude a non-synchronized method, nor a (synchronized or non-synchronized) static (class) method from running. Useful to know.
4. Communicating and Mutually-Exclusive Threads
Here's where things become downright complicated until you get familiar with the protocol. The hardest kind of threads programming is where the threads need to pass data back and forth to each other. Imagine that we are in the same situation as in the previous section: we have threads that process the same data, so we need to run synchronized. However, in our new case, imagine that it's not enough just to say "don't run while I am running". We need the threads to be able to say: "OK, I have some data ready for you" and to suspend themselves if there isn't data ready.
There's a convenient parallel programming idiom, known as wait/notify
that
does exactly this.
Wait/notify
is a tricky language-dependent protocol that has been developed by
ingenious minds. You just need to accept it as the right solution to your problem. It is used
when synchronized methods in the same class need to communicate with each other.
The most common occurrence of this is a producer/consumer situation - one thread is producing the data irregularly, and another thread is consuming (processing) it.
Usually the producer is storing the produced data intp some kind of bounded bufferm which means
that the produce may fill it up and will need to wait()
until there is room. The consumer
will need to notify()
the produce when something is removed from the buffer.
Here's the pseudo-code for the waitNotify.java
program below:
Wait and Notify://producer thread: produces one datum enter synchronized code (i.e., grab mutex lock) while (buffer_full) wait() // read below the semantics of wait (re: lock) produce_data notify() leave synchronized code (i.e., release lock)
//consumer thread: consumes one datum enter synchronized code (i.e., grab mutex lock) while (no_data) // buffer_empty wait() consume_data() notify leave synchronized code (i.e., release lock)
Oops, even though I have the lock I can't go any further until you have some data for me, so I will release the lock, and suspend myself here. One of you notifiers (grab the lock and) carry on!
Hey, I just produced some data, so I will release the lock and suspend myself here. One of you waiters (grab the lock from me and) carry on!
wait
and notify
are methods in the basic class
Object
so they are shared by all objects in the system. There
are several variants: public final native void notify(); public final native void notifyAll(); public final void wait () throws InterruptedException; public final void wait (long time, int nanos) throws InterruptedException; public final native void wait (long timeout) throws InterruptedException;The difference between
notify()
and notifyAll()
is that
the second one wakes up all threads that are in the wait list of this object.
tucotuco.cs.indiana.edu% ls -l total 10 -rw------- 1 dgerman students 661 Nov 12 12:01 drinks.java -rw-r--r-- 1 dgerman students 830 Nov 12 13:18 steamBoiler.java -rw------- 1 dgerman students 894 Nov 12 12:35 testPrime.java -rw-r--r-- 1 dgerman students 1620 Nov 12 14:31 waitNotify.java
There are three classes below.
The first is a class that contains a main driver program. It simply instantiates a producer thread and a consumer thread and lets them go at it.
tucotuco.cs.indiana.edu% cat waitNotify.java
public class waitNotify { public static void main(String args[]) { Producer p = new Producer(); p.start(); Consumer c = new Consumer(p); c.start(); } }
The second class is the Producer class. It implements the pseudo-code above, and
demonstrates the use of wait
/notify
. It has two key methods:
consume()
) will try to returns successive
values from this array. The value of this set-up is that produce()
and consume()
can be called from separate threads: they won't overrun
the array; they won't get something before it has been produced; they won't step on
each other; neither ever gets in a busy wait.
class Producer extends Thread { private String[] buffer = new String[8]; private int pi = 0; // produce index private int gi = 0; // get index public void run () { // just keep producing for (;;) produce(); } private final long start = System.currentTimeMillis(); private final String banana() { return "" + (int) (System.currentTimeMillis() - start); } synchronized void produce() { // while there isn't room in the buffer while (pi-gi + 1 > buffer.length) { try { wait(); } catch (Exception e) { } } buffer[pi&0x7] = banana(); System.out.println("produced[" + (pi&0x7) + "] " + buffer[pi&0x7]); pi++; notifyAll(); } synchronized String consume() { // while there's nothing left to take from the buffer while (pi == gi) { try { wait(); } catch (Exception e) { } } notifyAll(); return buffer[gi++&0x7]; // mask off the bits (lowest 3 bits - circular buffer) } }
The third class is another thread that will be the consumer in this example. It starts
off with a common Java idiom: another instance is passed into the constructor, and all
the constructor does is save a copy of this object for later use. This is the way that
the consumer can call the consume()
method of the producer.
The idiom of passing an object into a constructor that saves the reference to it, for later communicating something back, is a common idiom. We mentioned it when we discussed how chatters could register and later be notified in a chat system implemented with RMI.
class Consumer extends Thread { Producer whoIamTalkingTo; // java idiom for constructor Consumer (Producer who) { whoIamTalkingTo = who; }
public void run() { java.util.Random r = new java.util.Random(); for (;;) { String result = whoIamTalkingTo.consume(); System.out.println("consumed: " + result); // next line is just to make it run a bit slower int randomtime = r.nextInt() % 250; try { sleep(randomtime); } catch (Exception e) { } } } }Notice that the producer filled up the buffer before the consumer ran at all. Then each time the slow consumer removed something fromthe buffer, the producer re-used that now empty slot. And always the consumer got exactly what was stored there with no data race corruption.
tucotuco.cs.indiana.edu% javac waitNotify.java tucotuco.cs.indiana.edu% ls -l total 18 -rw-r--r-- 1 dgerman students 883 Nov 12 14:34 Consumer.class -rw-r--r-- 1 dgerman students 1411 Nov 12 14:34 Producer.class -rw------- 1 dgerman students 661 Nov 12 12:01 drinks.java -rw-r--r-- 1 dgerman students 830 Nov 12 13:18 steamBoiler.java -rw------- 1 dgerman students 894 Nov 12 12:35 testPrime.java -rw-r--r-- 1 dgerman students 451 Nov 12 14:34 waitNotify.class -rw-r--r-- 1 dgerman students 1620 Nov 12 14:31 waitNotify.java tucotuco.cs.indiana.edu% java waitNotify produced[0] 9 produced[1] 44 produced[2] 46 produced[3] 50 produced[4] 53 produced[5] 56 produced[6] 59 produced[7] 62 consumed: 9 produced[0] 105 produced[1] 116 consumed: 44 produced[2] 282 consumed: 46 produced[3] 289 consumed: 50 produced[4] 297 consumed: 53 produced[5] 304 consumed: 56 ^Ctucotuco.cs.indiana.edu%
We can run it again.
tucotuco.cs.indiana.edu% java waitNotify <----( stopped, run again )---- produced[0] 9 produced[1] 44 produced[2] 48 produced[3] 52 produced[4] 55 produced[5] 58 produced[6] 61 produced[7] 63 consumed: 9 produced[0] 102 consumed: 44 produced[1] 251 consumed: 48 produced[2] 379 consumed: 52 produced[3] 608 consumed: 55 produced[4] 659 produced[5] 668 <------- this sequence is particularly interesting consumed: 58 <------- the race is at the standard output! But all is well... produced[6] 675 consumed: 61 produced[7] 766 consumed: 63 produced[0] 773 consumed: 102 produced[1] 780 consumed: 251 produced[2] 976 consumed: 379 produced[3] 983 consumed: 608 produced[4] 1007 consumed: 659 produced[5] 1116 consumed: 668 produced[6] 1123 consumed: 675 produced[7] 1156 consumed: 766 produced[0] 1162 consumed: 773 produced[1] 1169 consumed: 780 produced[2] 1175 consumed: 976 produced[3] 1386 consumed: 983 produced[4] 1556 consumed: 1007 produced[5] 1796 consumed: 1116 produced[6] 1803 consumed: 1123 ^Ctucotuco.cs.indiana.edu% java waitNotify <----( stopped, run again )---- produced[0] 9 produced[1] 44 produced[2] 47 produced[3] 50 produced[4] 53 produced[5] 56 produced[6] 59 produced[7] 62 consumed: 9 produced[0] 98 consumed: 44 produced[1] 112 consumed: 47 consumed: 50 consumed: 53 consumed: 56 produced[2] 266 produced[3] 268 produced[4] 271 produced[5] 274 consumed: 59 produced[6] 324 consumed: 62 produced[7] 474 consumed: 98 produced[0] 634 consumed: 112 produced[1] 834 consumed: 266 consumed: 268 consumed: 271 produced[2] 880 produced[3] 883 produced[4] 886 consumed: 274 consumed: 324 consumed: 474 consumed: 634 produced[5] 933 produced[6] 936 produced[7] 939 produced[0] 942 consumed: 834 consumed: 880 consumed: 883 produced[1] 1030 produced[2] 1033 produced[3] 1036 consumed: 886 produced[4] 1064 consumed: 933 produced[5] 1114 consumed: 936 produced[6] 1364 consumed: 939 consumed: 942 produced[7] 1514 produced[0] 1515 consumed: 1030 produced[1] 1622 consumed: 1033 produced[2] 1802 consumed: 1036 produced[3] 1922 consumed: 1064 consumed: 1114 produced[4] 2034 produced[5] 2035 consumed: 1364 produced[6] 2272 consumed: 1514 consumed: 1515 consumed: 1622 consumed: 1802 consumed: 1922 produced[7] 2299 produced[0] 2301 produced[1] 2302 produced[2] 2303 produced[3] 2304 consumed: 2034 produced[4] 2512 consumed: 2035 consumed: 2272 consumed: 2299 consumed: 2301 consumed: 2302 produced[5] 2588 produced[6] 2589 produced[7] 2590 produced[0] 2592 produced[1] 2593 consumed: 2303 produced[2] 2833 consumed: 2304 consumed: 2512 consumed: 2588 consumed: 2589 produced[3] 2917 produced[4] 2918 produced[5] 2919 produced[6] 2921 consumed: 2590 produced[7] 2932 ^Ctucotuco.cs.indiana.edu%Notes
1. Code like this:
acknowledges the fact that one thread can interrupt another swleeping thread by calling itstry { sleep (randomtime); } catch (Exception e) { } try { wait(); } catch(Exception e) {}
interrupt()
method. This will make the interrupted thread wake up. It really
needs to tell the difference between waking up because it has been "notified" and waking up
because it has been "interrupted". So the second case is detected by raising the exception
InterruptedException
in the thread, Statements like sleep()
and
wait()
that are potentially prone to being interrupted in the middle need to
catch this exception. 2. Always catch the narrowest type of exception you can or you may catch more than you bargained for. And always do something sensible with it, like print an error message.
3. Synchronized code isn't a perfect solution, you need to make sure you avoid deadlock.