CS 596 Java Programming Fall Semester, 1998 Threads part 2 |
||
---|---|---|
© 1998, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 13-Nov-98 |
Thread Control
So far we have just started threads and let them run independently. Threads often have to work together. One thread may need the result of some computation of another thread. The first thread will have to wait until the second one has completed the computation. On a single processor, you need to make sure that one thread does not consume all the CPU cycles, leaving other threads waiting for their turn.
A thread can be in one of three states: executing (running), runnable (but not executing), not runnable. On a multi-processor machine more than one thread may be executing at the same time. A thread that is waiting for another thread, suspended, or sleeping is not runnable.
Methods of Interest
destroy()
join()
stop()
interrupt()
join(long)
stop(Throwable)
interrupted()
join(long, int)
suspend()
isInterrupted()
sleep(long)
yield()
sleep(long, int)
Threads Run Once
Once a thread's run method ends, the thread can not be restarted. You can call the thread's run method directly, but that just sequentially executes that method.
class SimpleThread extends Thread { public void run() { System.out.println( "I ran" ); } }
public class Test { public static void main( String args[] ) throws Exception { SimpleThread onceOnly = new SimpleThread(); onceOnly.setPriority( 6 ); onceOnly.start(); System.out.println( "Try restart"); onceOnly.start(); System.out.println( "The End"); } }Output I ran Try restart The End
Yield
Sending the "yield()" method to a thread moves it from the executing state to the runnable state. Often the executing thread sends the "yield()" method to itself. This allows other runnable threads of the same priority a chance to execute.
class YieldThread extends Thread { public void run() { for ( int count = 0; count < 4; count++) { System.out.println( count + "From: " + getName() ); yield(); } } } class TestThread { public static void main( String[] args ) { YieldThread first = new YieldThread(); YieldThread second = new YieldThread(); first.start(); second.start(); System.out.println( "End" ); } }Output - Single and Multiple Processors End 0From: Thread-2 0From: Thread-3 1From: Thread-2 1From: Thread-3 2From: Thread-2 2From: Thread-3 3From: Thread-2 3From: Thread-3
Sleep
The sleep() method moves a thread to the not runnable state for specified length of time. At the end of that time, the thread is moved to the runnable state. If it has a higher priority than the current executing thread, the executing thread is moved to the runnable state and the just awakened thread becomes the executing thread.
class SleepyThread extends Thread { int maxCount = 4; public SleepyThread( String name, int count ) { super( name ); maxCount = count; } public void run() { for ( int count = 0; count < 4; count++) { System.out.println( count + " From: " + getName() ); try { sleep( 5 ); // in milliseconds } catch ( InterruptedException ignored ) {} } } }
// Sleep Example Continued
class TestSleep { public static void main( String[] args ) { SimpleThread alert = new SimpleThread( "Alert", 32 ); SleepyThread sleepy = new SleepyThread( "Sleepy", 32 ); sleepy.setPriority( 8 ); alert.start(); sleepy.start(); System.out.println( "End" ); } }Output (read down first)
0 From: Sleepy | 17 From: Alert |
0 From: Alert | 18 From: Alert |
1 From: Alert | 19 From: Alert |
2 From: Alert | 20 From: Alert |
3 From: Alert | 21 From: Alert |
4 From: Alert | 22 From: Alert |
5 From: Alert | 23 From: Alert |
6 From: Alert | 24 From: Alert |
7 From: Alert | 25 From: Alert |
8 From: Alert | 26 From: Alert |
9 From: Alert | 27 From: Alert |
10 From: Alert | 28 From: Alert |
11 From: Alert | 2 From: Sleepy |
12 From: Alert | 29 From: Alert |
13 From: Alert | 30 From: Alert |
14 From: Alert | 31 From: Alert |
1 From: Sleepy | 3 From: Sleepy |
End | |
15 From: Alert | |
16 From: Alert |
A Clock Example Using the sleep method and a thread, we can make a clock. The following clock can be used in a simulation. It allows us to simulate a given number of days in an hour.
import java.util.Date; class Clock extends Thread { static int millisecondsHour = 1000 * 60 * 60; static long millisecondsPerDay = millisecondsHour * 24; int millSecondsPerSimDay; Date simDate = new Date(); public Clock( int simDaysPerHour ) { millSecondsPerSimDay = millisecondsHour / simDaysPerHour; setPriority( Thread.MAX_PRIORITY ); } public String toString() { return simDate.toString(); } public void run() { try { while (true) { // Advance one sim day long simTime = simDate.getTime(); simDate.setTime( simTime + millisecondsPerDay); // wait until next day sleep( millSecondsPerSimDay ); } } catch ( InterruptedException simulationOver ) { return;} } }
Using the Clock import sdsu.io.Console; class Test { public static void main(String args[] ) throws Exception { Clock fast = new Clock( 7 * 52 * 60 ); fast.start(); for ( int k = 0; k < 5; k++ ) { System.out.println( fast.toString()); Console.readInt( "Type an int" ); } fast.stop(); } }Output rohan 24-> java Test Tue Nov 03 09:37:55 PST 1998 Type an int 12 Sun Nov 29 09:37:55 PST 1998 Type an int 32 Fri Jan 29 09:37:55 PST 1999 Type an int 12 Sat Apr 17 10:37:55 PDT 1999 Type an int 10 Thu Jan 20 09:37:55 PST 2000 Type an int 10
Join - Waiting for Thread to end
If thread A sends the "join()" method to thread B, then thread A will be "not runnable" until thread B's run methods ends. At that time thread A becomes runnable.
class Godot { public static void main( String[] args ) throws InterruptedException { SimpleThread lowly = new SimpleThread( "Lowly" ); lowly.setPriority( 1 ); lowly.start(); System.out.println( "now I can go" ); lowly.join(); System.out.println( "Done" ); } }Output (Read down first)
now I can go | 11 From: Lowly | 23 From: Lowly |
0 From: Lowly | 12 From: Lowly | 24 From: Lowly |
1 From: Lowly | 13 From: Lowly | 25 From: Lowly |
2 From: Lowly | 14 From: Lowly | 26 From: Lowly |
3 From: Lowly | 15 From: Lowly | 27 From: Lowly |
4 From: Lowly | 16 From: Lowly | 28 From: Lowly |
5 From: Lowly | 17 From: Lowly | 29 From: Lowly |
6 From: Lowly | 18 From: Lowly | 30 From: Lowly |
7 From: Lowly | 19 From: Lowly | 31 From: Lowly |
8 From: Lowly | 20 From: Lowly | Done |
9 From: Lowly | 21 From: Lowly | |
10 From: Lowly | 22 From: Lowly |
Ending Thread Execution
A thread ends when its run method ends. At times program needs to permanently stop a running thread. For example, when a user uses a browser to access a web page. The browser will open a new network connection in a separate thread. If the user then cancels the request, the browser may need to "kill" the thread doing the down load.
Suspend, Resume, Stop, destroy
Java has some thread methods that will stop threads. However, these methods are not safe! They could leave your program in an inconsistent state or cause deadlock. Suspend, resume and stop do exist, but are not safe. They are deprecated in JDK 1.2. Destroy, while listed in the on-line API, was never implemented. It throws a NoSuchMethodError() in JDK 1.2.
These methods are deprecated in JDK 1.2 because they are not thread safe
Start avoiding these methods now!
What replaces Stop? It turns out that there is no safe way to implement a method that will stop a thread in general. Doug Lea recommends a multiphase approach. First, use interrupt. If that fails, try starving the thread. If that also fails, giving the thread the minimum priority to reduce its impact. If these all fail and the situation calls for drastic action, then one can use stop(), perform clean up operations, then exit the program.
Forcing a Thread to Quit - Using interrupt()
A thread can perform a block of operations then check to see if it is interrupted. If it has been interrupted, then it can take "proper" action. Sometimes proper action is to clean up, then quit. Sometimes proper action may be to "reset itself" to be available to run again later. In the example below the sleep() method is throwing the InterruptedException.
class NiceThread extends Thread { public void run() { while ( !isInterrupted() ) { System.out.println( "From: " + getName() ); } System.out.println( "Clean up operations" ); } }
public class Test { public static void main(String args[]) throws InterruptedException{ NiceThread missManners = new NiceThread( ); missManners.setPriority( 2 ); missManners.start(); Thread.currentThread().sleep( 5 ); // Let other thread run missManners.interrupt(); } }Output From: Thread-3 From: Thread-3 From: Thread-3 From: Thread-3 From: Thread-3 Clean up operations
Interrupt Methods in java.lang.Thread
void interrupt()
Using Thread.interrupted
This example uses the test Thread.interrupted() to allow the thread to be continue execution later. Note that thread uses suspend() after it has made sure that all data is safe. This is harder to do in real life than the simple example here indicates. Using wait would be better here, but we have not covered wait() yet.
class RepeatableNiceThread extends Thread { public void run() { while ( true ) { while ( !Thread.interrupted() ) System.out.println( "From: " + getName() ); System.out.println( "Clean up operations" ); suspend(); } } }
public class Test { public static void main(String args[]) throws InterruptedException{ RepeatableNiceThread missManners = new RepeatableNiceThread( ); missManners.setPriority( 2 ); missManners.start(); Thread.currentThread().sleep( 5 ); missManners.interrupt(); missManners.resume(); Thread.currentThread().sleep( 5 ); missManners.interrupt(); } }
Interrupt and sleep, join & wait
Let thread A be in the not runnable state due to being sent either the sleep(), join() or wait() methods. Then if thread A is sent the interrupt() method, it is moved to the runnable state and InterruptedException is raised in thread A.
In the example below, NiceThread puts itself to sleep. While asleep it is sent the interrupt() method. The code then executes the catch block.
class NiceThread extends Thread { public void run() { try { while ( !isInterrupted() ) { System.out.println( "From: " + getName() ); sleep( 5000 ); } System.out.println( "Clean up operations" ); } catch ( InterruptedException interrupted ) { System.out.println( "In catch" ); } } } public class Test { public static void main( String args[] ) { NiceThread missManners = new NiceThread( ); missManners.setPriority( 6 ); missManners.start(); missManners.interrupt(); } }Output From: Thread-1 In catch
Who Sends sleep() is Important
Since main sends the sleep method, not the thread itself, the InterruptedException is not thrown.
public class Test { public static void main( String args[] ) { try { NiceThread missManners = new NiceThread( ); missManners.setPriority( 1 ); missManners.start(); missManners.sleep( 5000); missManners.interrupt(); } catch ( InterruptedException interrupted ) { System.out.println( "Caught napping" ); } } } class NiceThread extends Thread { public void run() { try { while ( !isInterrupted() ) { System.out.println( "From: " + getName() ); } System.out.println( "Clean up operations" ); } catch ( Exception interrupted ) { System.out.println( "In catch" ); } } }Output From: Thread-1 From: Thread-1 From: Thread-1 From: Thread-1 Clean up operations
IO Blocks
A read() on an inputstream or reader blocks. Once a thread calls read() it will not respond to interrupt() (or much else) until the read is completed. This is a problem when a read could take a long time: reading from a socket or the keyboard. If the input is not forth coming, the read() could block forever. NonBlockingInputStream on the next slide will timeout and respond to interrupt.
NonBlockingInputStream public class NonBlockingInputStream extends FilterInputStream { private int pollDuration; private long timeout; public NonBlockingInputStream( InputStream input, int pollDelay, long timeout ){ super( input ); pollDuration = pollDelay; this.timeout = timeout; } public int read() throws IOException { pollInput(); return in.read(); } public int read(byte inputBuffer[], int offset, int length) throws IOException { pollInput(); return in.read( inputBuffer, offset, length); } private void pollInput() throws IOException { long startTime = System.currentTimeMillis(); while ( in.available() <= 0 ) try { if ( System.currentTimeMillis() - startTime >= timeout ) throw new IOException( "Timeout on read"); Thread.sleep( pollDuration ); } catch ( InterruptedException stopReading ) { throw new InterruptedIOException( "read() interrupted" ); } } }
Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.
visitors since 02-Nov-98