CS 596: Client-Server Programming
Spring Semester, 1997
Doc 7, Threads
To Lecture Notes Index
San Diego State University -- This page last updated Feb 13, 1997
Contents of Doc 7, Threads
- Threads
- Creating Threads
- Thread Scheduling - Preemptive
- Simple Yield Example
- Sleep Example
- Suspend and Resume
- Join - Waiting for Thread to end
- Synchronize
- Synchonized and Inheritance
- wait and notify
- wait - How to use
- notify - How to Use
Thread is a separate process which shares the same name space as the program
that created the thread
Difference from fork()
fork()
- Child process gets a copy of parents variables
- Relatively expensive to start
- Don't have to worry about concurrent access to variables
thread
- Child process shares parents variables
- Relatively cheap to start
- Concurrent access to variables is an issue
Thread Class Methods
activeCount() | interrupt() | setDaemon(boolean) |
checkAccess() | interrupted() | setName(String) |
countStackFrames() | isAlive() | setPriority(int) |
currentThread() | isDaemon() | sleep(long) |
destroy() | isInterrupted() | sleep(long, int) |
dumpStack() | join() | start() |
enumerate(Thread[]) | join(long) | stop() |
getName() | join(long, int) | stop(Throwable) |
getPriority() | resume() | suspend() |
getThreadGroup() | run() | toString() |
| | yeild() |
There are two different methods for creating a thread
class SimpleThread extends Thread
{
public void run()
{
for ( int count = 0; count < 4; count++)
System.out.println( "Message " + count +
"From: Mom" );
}
}
class TestThread
{
public static void main( String[] args )
{
SimpleThread parallel = new SimpleThread();
System.out.println( "Create the thread");
parallel.start();
System.out.println( "Started the thread" );
System.out.println( "End" );
}
}
Output
Create the thread
Started the thread
End
Message 0From: Mom
Message 1From: Mom
Message 2From: Mom
Message 3From: Mom
Message 4From: Mom
Second Method for Creating a Thread
class SecondMethod implements Runnable
{
public void run()
{
for ( int count = 0; count < 4; count++)
System.out.println( "Message " + count + " From: Dad");
}
}
class TestThread
{
public static void main( String[] args )
{
SecondMethod notAThread = new SecondMethod();
Thread parallel = new Thread( notAThread );
System.out.println( "Create the thread");
parallel.start();
System.out.println( "Started the thread" );
System.out.println( "End" );
}
}
Output
Create the thread
Started the thread
End
Message 0 From: Dad
Message 1 From: Dad
Message 2 From: Dad
Message 3 From: Dad
Giving a Thread a Name
class SecondMethod implements Runnable
{
public void run()
{
for ( int count = 0; count < 4; count++)
System.out.println( "Message " + count + " From: " +
Thread.currentThread().getName() );
}
}
class TestThread
{
public static void main( String[] args )
{
SecondMethod notAThread = new SecondMethod();
Thread parallel = new Thread( notAThread, "Mom" );
System.out.println( "Create the thread");
parallel.start();
System.out.println( "Started the thread" );
System.out.println( "End" );
}
}
Output
Create the thread
Started the thread
End
Message 0 From: Mom
Message 1 From: Mom
Message 2 From: Mom
Message 3 From: Mom
SimpleThread for Use in Future Examples
class SimpleThread extends Thread
{
private int maxCount = 32;
public SimpleThread( String name )
{
super( name );
}
public SimpleThread( String name, int repetitions )
{
super( name );
maxCount = repetitions;
}
public SimpleThread( int repetitions )
{
maxCount = repetitions;
}
public void run()
{
for ( int count = 0; count < maxCount; count++)
{
System.out.println( count + " From: " + getName() );
}
}
}
Show Some Parallelism?
class TestThread
{
public static void main( String[] args )
{
SimpleThread first = new SimpleThread( 5 );
SimpleThread second = new SimpleThread( 5 );
first.start();
second.start();
System.out.println( "End" );
}
}
Output
End
0 From: Thread-4
1 From: Thread-4
2 From: Thread-4
3 From: Thread-4
4 From: Thread-4
0 From: Thread-5
1 From: Thread-5
2 From: Thread-5
3 From: Thread-5
4 From: Thread-5
Priorities
A thread is run before other threads with lower priorities
- public final static int MIN_PRIORITY = 1
- public final static int NORM_PRIORITY = 5
- public final static int MAX_PRIORITY = 10
class TestThread
{
public static void main( String[] args )
{
SimpleThread first = new SimpleThread( 5 );
SimpleThread second = new SimpleThread( 5 );
second.setPriority( 8 );
first.start();
second.start();
System.out.println( "End" );
}
}
Output
0 From: Thread-5
1 From: Thread-5
2 From: Thread-5
3 From: Thread-5
4 From: Thread-5
0 From: Thread-4
1 From: Thread-4
2 From: Thread-4
3 From: Thread-4
4 From: Thread-4
End
Thread SchedulingTime-slicing or not time-sliced
Time-slicing
- a thread is run for a short time slice and suspended,
- resumes only when it gets its next "turn"
-
- threads of the same priority share turns
Java does not specify if the scheduler does time-slicing
Java on Solaris does not time-slice
Non time-sliced threads run until:
- they end
- they are terminated
- they are interrupted
- Higher priority threads interrupts lower priority threads
- they go to sleep
- they block on some call
- reading a socket
- waiting for another thread
- they yield
Methods of Interest
destroy() | sleep(long) |
interrupt() | sleep(long, int) |
join() | stop() |
join(long) | stop(Throwable) |
join(long, int) | suspend() |
| yield() |
Testing for Time-slicing
class InfinityThread extends Thread
{
public void run()
{
while ( true )
System.out.println( "From: " + getName() );
}
}
class TestThread
{
public static void main( String[] args )
{
InfinityThread first = new InfinityThread( );
InfinityThread second = new InfinityThread( );
first.start();
second.start();
}
}
Output if Time-sliced
"From: Thread-5" and "From: Thread-4" will intermix "forever"
Output if not Time-sliced
"From: Thread-4" will repeat "forever"
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
End
0 From: Thread-4
0 From: Thread-5
1 From: Thread-4
1 From: Thread-5
2 From: Thread-4
2 From: Thread-5
3 From: Thread-4
3 From: Thread-5
class SleepyThread extends Thread
{
public SleepyThread( String name )
{
super( name );
}
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
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
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 Clock
import sdsu.io.Console;
import java.util.Date;
class Test
{
public static void main(String args[] ) throws Exception
{
Clock fast = new Clock( 60 * 60 * 10 );
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
Thu Feb 13 17:19:11 PST 1997
Type an int 12
Fri Feb 28 17:19:11 PST 1997
Type an int 21
Mon Mar 24 17:19:11 PST 1997
Type an int 12
Thu Apr 10 18:19:11 PDT 1997
Type an int 12
Fri May 02 18:19:11 PDT 1997
Type an int 12
Setting Priorities
Continuously running parts of the program should have lower-priority than rarer
events
User input should have very high priority
A thread that continually updates some data is often set to run at
MIN_PRIORITY
class Suspended
{
public static void main( String[] args ) throws
InterruptedException
{
SimpleThread alert = new SimpleThread( "Alert" );
SleepyThread sleepy = new SleepyThread( "Sleepy" );
sleepy.setPriority( 8 );
alert.start();
sleepy.start();
sleepy.suspend();
System.out.println( "now I can go" );
Thread.sleep( 4 );
sleepy.resume();
}
}
Output
0 From: Sleepy | 12 From: Alert | 26 From: Alert |
0 From: Alert | 13 From: Alert | 27 From: Alert |
1 From: Alert | 14 From: Alert | 28 From: Alert |
2 From: Alert | 15 From: Alert | 29 From: Alert |
3 From: Alert | 16 From: Alert | 30 From: Alert |
4 From: Alert | 17 From: Alert | 31 From: Alert |
5 From: Alert | 18 From: Alert | 3 From: Sleepy |
6 From: Alert | 19 From: Alert | |
7 From: Alert | 20 From: Alert | |
8 From: Alert | 21 From: Alert | |
9 From: Alert | 22 From: Alert | |
1 From: Sleepy | 23 From: Alert | |
now I can go | 24 From: Alert | |
10 From: Alert | 2 From: Sleepy | |
11 From: Alert | 25 From: Alert | |
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
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 | |
With multiprocessing we need to address mutual exclusion
What happens when two threads access the same:
- method?
-
- data?
class AccessExample
{
public int[] data = new int[10];
public safeCode( int Size, int startValue)
{
int[] verySafe = new int[ Size ];
for ( int index = 0; index < Size; index++ )
verySafe[ index ] = (int ) Math.sin( index * startValue );
}
public dangerousCode( int Size, int startValue)
{
for ( int index = 0; index < Size; index++ )
data[ index ] = (int ) Math.sin( index * startValue );
}
}
Java uses "Synchronized" code to insure that only one thread at a time will use
a section of critical code
Synchronize Syntax
class SynchronizeSyntax
{
protected int myValue;
public static int globalStuff;
public synchronized void synchronizedMethod()
{
myValue++;
}
public ShowSynchronizedBlock( Vector criticalData )
{
// Some non-critical code can go here
synchronized ( criticalData )
{
// put critical code relating to criticalData here
}
// More non-critical code can go here
}
public SynchronizedClass( )
{
// Some non-critical code can go here
synchronized ( getClass() )
{
// put critical code relating to class SynchronizeSyntax
}
// More non-critical code can go here
}
}
Synchronized Example
class Loop
{
public void doLoop( int size, String name )
{
for ( int count = 0; count < size; count++)
{
System.out.println( count + " From: " + name );
double wasteTime = Math.sin( Math.cos ( count ) );
}
}
}
class SyncLoop extends Loop
{
public synchronized void doLoop( int size, String name )
{
for ( int count = 0; count < size; count++)
{
System.out.println( count + " From: " + name );
double wasteTime = Math.sin( Math.cos( count ) );
}
}
}
// Synchronized Example Continued
class SimpleThread extends Thread
{
private int maxCount;
private Loop myCopyOfLoop;
public SimpleThread( String name, int count, Loop copy )
{
super( name );
maxCount = count;
myCopyOfLoop = copy;
}
public void run()
{
myCopyOfLoop.doLoop( maxCount, getName() );
}
}
// Synchronized Example Continued
class SleepyThread extends Thread
{
private Loop myCopyOfLoop;
private int maxCount;
public SleepyThread( String name, int count, Loop copy )
{
super( name );
maxCount = count;
myCopyOfLoop = copy;
}
public void run()
{
for ( int count = 0; count < 4; count++)
{
try
{
sleep( 5 ); // in milliseconds
myCopyOfLoop.doLoop( maxCount, getName() );
}
catch ( InterruptedException ignored ) {}
}
}
}
// Synchronized Example Continued
class TestNoSynch
{
public static void main( String[] args ) throws Exception
{
Loop notSafe = new Loop();
SimpleThread alert;
SleepyThread sleepy;
alert = new SimpleThread( "Alert", 8, notSafe );
sleepy = new SleepyThread( "Sleepy", 2, notSafe );
sleepy.setPriority( 8 );
alert.start();
sleepy.start();
System.out.println( "End" );
}
}
Output
0 From: Alert | 6 From: Alert |
1 From: Alert | 7 From: Alert |
2 From: Alert | 0 From: Sleepy |
0 From: Sleepy | 1 From: Sleepy |
1 From: Sleepy | 0 From: Sleepy |
End | 1 From: Sleepy |
3 From: Alert | 0 From: Sleepy |
4 From: Alert | 1 From: Sleepy |
5 From: Alert | |
// Synchronized Example Continued
class TestSynch
{
public static void main( String[] args ) throws Exception
{
Loop safe = new SyncLoop();
SimpleThread alert;
SleepyThread sleepy;
alert = new SimpleThread( "Alert", 8, safe );
sleepy = new SleepyThread( "Sleepy", 2, safe );
sleepy.setPriority( 8 );
alert.start();
sleepy.start();
System.out.println( "End" );
}
}
Output
0 From: Alert | 1 From: Sleepy |
1 From: Alert | End |
2 From: Alert | 0 From: Sleepy |
3 From: Alert | 1 From: Sleepy |
4 From: Alert | 0 From: Sleepy |
5 From: Alert | 1 From: Sleepy |
6 From: Alert | 0 From: Sleepy |
7 From: Alert | 1 From: Sleepy |
0 From: Sleepy | |
class Top
{
public void synchronized left()
{
// do stuff
}
public void synchronized right()
{
// do stuff
}
}
class Bottom extends Top
{
public void left()
{
// not synchronized
}
public void right()
{
// do stuff not synchronized
super.right(); // synchronized here
// do stuff not synchronized
}
If you want a method in a subclass to be synchronized you must declare it to be synchronized.
Synchonized and Static Methods
Two threads can not execute synchronized static methods on the same class at
the same time.
The lock for a static method has no effect on any of the objects of that
class.
class Queue
{
LinkedCell head;
LinkedCell tail;
public synchronized void append( Object item )
{
LinkedCell newItem = new LinkedCell( item );
if ( head == null )
head = newItem;
else
tail.append( newItem );
tail = newItem;
notify();
}
public synchronized Object get( )
{
try
{
while ( head == null )
wait();
}
catch (InterruptedException threadIsDone )
{
return null;
}
Object returnMe = head.element();
head = head.next();
return returnMe;
}
}
wait and notify - Helper
class LinkedCell
{
Object cellItem;
LinkedCell next;
public LinkedCell( Object value )
{
cellItem= value;
}
public Object element()
{
return cellItem;
}
public void append( LinkedCell nextCell )
{
next = nextCell;
}
public LinkedCell next()
{
return next;
}
}
wait and notify - Producer
class Producer extends Thread
{
Queue factory;
int workSpeed;
public Producer( String name, Queue output, int speed )
{
setName(name);
factory = output;
workSpeed = speed;
}
public void run()
{
try
{
int product = 0;
while (true) // work forever
{
System.out.println( getName() + " produced " + product);
factory.append( getName() + String.valueOf( product) );
product++;
sleep( workSpeed);
}
}
catch ( InterruptedException WorkedToDeath )
{
return;
}
}
}
wait and notify - Consumer
class Consumer extends Thread
{
Queue localMall;
int sleepDuration;
public Consumer( String name, Queue input, int speed )
{
setName(name);
localMall = input;
sleepDuration = speed;
}
public void run()
{
try
{
while (true) // Shop until you drop
{
System.out.println( getName() + " got " +
localMall.get());
sleep( sleepDuration );
}
}
catch ( InterruptedException endOfCreditCard )
{
return;
}
}
}
wait and notify - Driver Program
class Test
{
public static void main( String args[] ) throws Exception
{
Queue wallmart = new Queue();
Producer nike = new Producer( "Nike", wallmart, 500 );
Producer honda = new Producer( "Honda", wallmart, 1200 );
Consumer valleyGirl = new Consumer( "Sue", wallmart, 400);
Consumer valleyBoy = new Consumer( "Bob", wallmart, 900);
Consumer dink = new Consumer( "Sam", wallmart, 2200);
nike.start();
honda.start();
valleyGirl.start();
valleyBoy.start();
dink.start();
}
}
Output
Nike produced 0 | Sue got Nike3 | Honda produced 3 |
Honda produced 0 | Nike produced 4 | Bob got Honda3 |
Sue got Nike0 | Sue got Nike4 | Nike produced 8 |
Bob got Honda0 | Honda produced | Sue got Nike8 |
Nike produced 1 | Bob got Honda2 | Nike produced 9 |
Sam got Nike1 | Nike produced 5 | Sue got Nike9 |
Nike produced 2 | Sue got Nike5 | Honda produced 4 |
Sue got Nike2 | Nike produced 6 | Bob got Honda4 |
Honda produced 1 | Sam got Nike6 | Nike produced 10 |
Bob got Honda1 | Nike produced 7 | Sue got Nike10 |
Nike produced 3 | Sue got Nike7 | Nike produced 11 |
The thread waiting for a condition should look like:
synchronized void waitingMethod()
{
while ( ! condition )
wait();
Now do what you need to do when condition is true
}
Note
Everything is executed in a synchronized method
The test condition is in loop not in an if statement
The wait suspends the thread it atomically releases the lock on the object
synchronized void changeMethod()
{
Change some value used in a condition test
notify();
}
wait and notify Methodsin Object
public final void wait(timeout) throws InterruptedException
- Causes a thread to wait until it is notified or the specified timeout
expires.
- Parameters:
- timeout - the maximum time to wait in milliseconds
-
- Throws: IllegalMonitorStateException
- If the current thread is not the owner of the Object's monitor.
- Throws: InterruptedException
- Another thread has interrupted this thread.
public final void wait(timeout, nanos) throws InterruptedException
public final void wait() throws InterruptedException
public final void notify()
public final void notifyAll()
- Notifies all of the threads waiting for a condition to change. Threads that
are waiting are generally waiting for another thread to change some condition.
Thus, the thread effecting a change that more than one thread is waiting for
notifies all the waiting threads using the method notifyAll(). Threads that
want to wait for a condition to change before proceeding can call wait(). The
method notifyAll() can only be called from within a synchronized method.