Survey
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
Thread Summary
Doug Skuce
School of Information Technology and Engineering
University of Ottawa
5/8/2017 4:58 PM
Thread Introduction
A thread is an instruction sequence designed to possibly execute concurrently. It is
defined in a program, e.g. in Java, and becomes part of a process in an operating system,
e.g. Windows or Unix. Like a process, it has various states that it cycles through in its life
cycle. In Java, you must define a thread using a class, instantiate the class, and then call
the start() method. The thread will then execute its run() method, like a Java program
executes its main method. Several threads normally run in parallel, and both the Java
Runtime System and the operating system participate in scheduling their use of one or
more cpus.
thread –(
-- definition: an instruction sequence designed to possibly execute concurrently.
source: DS
-- definition: a single sequential flow of control within a program. source: Sun
Java Tutorial.
-- definition: an object that is an instance of the Thread class.
-- purposes: (handling asynchronous I/O, improving utilization of cpu, distribuiting
a problem over multiple cpus).
-- has a thread state.
-- has a default name.
-- has a priority. comment: inherited from its parent.
-- has a call stack.
-- can be defined in a Java program.
-- can hold 0 or more locks.
-- can run on 1 or more cpus.
-- can be part of thread group.
-- has a parent (thread or process) P.
-- inherits its (cpu access and memory) from P.
-- run in the same address space as other threads of the defining Java program.
-- has a run method. similar: a main method.
-- is controlled by the (Java Runtime System and the operating system.).
asynchronous means: having a time relationship that does not involve waiting.
1
to define a thread you can subclass Thread. example:
public class MyThread extends Thread{ public void run() {
run method goes here}};
to define a thread you can implement Runnable. example:
public class MyRunnable implements Runnable { public void
run() {run method goes here}};
comment: this way of creating threads is better.
if you pass a Runnable object (O) to a thread constructor then O is called the
target.
a Runnable object cannot be started.
run() cannot be called directly; it must be public, it cannot throw an exception.
to create a thread syntax: threadVar = new ThreadConstructor.
to create a thread example: myThread = new Thread(“Thread Name”).
to create a thread example: myThread = new Thread(new
MyRunnable());comment: note that a Runnable object must be created first and
passed to the Thread constructor using this technique.
to start a thread you must call its start method; example: myThread.start().
comment: start() calls the run() method of the (Thread object or Runnable
object).
to terminate a thread you exit its run method.
run() can be overloaded.
main() is an instance of a thread.
the order of starting threads does not affect the order that they execute.
most Java Virtual Machines map threads to native threads in the operating
system.
Thread States
there are 5 instances of a thread state: New, Ready, Running, Not Runnable,
Dead.
2
new
NEW
start
READY
yield or
time slice
ends or
preempted
notify or
I/O done or
interrupted
scheduler
chooses this
thread
RUNNING
NOT
RUNNABLE
sleep or
wait for I/O,
lock, join or
notify
finish run
method or
uncaught
exception
DEAD
New This is the state the thread is in after the Thread instance has been instantiated, but the
start() method has not been invoked on the thread. A Thread object exists to hold the
thread’s state information, but it is not yet a thread of execution. At this point, the thread is
considered not alive.
Ready This is the state a thread is in when it’s eligible to run, but the scheduler has not selected
it to be the running thread. A thread first enters the ready state when the start() method is
invoked, but a thread can also return to the ready state after either running or coming back from
a blocked, waiting, or sleeping state. When the thread is in the ready state, it is considered alive.
Running This is the state a thread is in when the thread scheduler selects it (from the ready pool)
to be the currently executing process. A thread can transition out of a running state for several
reasons, including because “the thread scheduler felt like it.” We’ll look at those other reasons
shortly. Note that there are several ways to get to the ready state, but only one way to get to the
running state: the scheduler chooses a thread from the ready pool.
Not Runnable = Waiting/blocked/sleeping OK, so this is really three states combined into one,
but they all have one thing in common: the thread is still alive, but is currently not eligible to run.
In other words, it is not ready, but it might return to a ready state later if a particular event occurs.
A thread may be blocked waiting for a resource (like I/O or an object’s lock), in which case the
event that sends it back to ready is the availability of the resource—for example, if data comes in
through the input stream the thread code is reading from, or if the object’s lock suddenly becomes
available. A thread may be sleeping because the thread’s run code tells it to sleep for some period
of time, in which case the event that sends it back to ready is that it wakes up because its sleep
time has expired. Or the thread may be waiting, because the thread’s run code causes it to wait, in
which case the event that sends it back to ready is that another thread sends a notification that it
may no longer be necessary for the thread to wait. The important point is that one thread does not
3
tell another thread to block. Note also that a thread in a blocked state is still considered to be
alive.
Dead A thread is considered dead when its run() method completes. There may still be a
Thread object, but it is no longer a separate thread of execution. Once a thread is dead, it can
never be brought back to life! If you invoke start() on a dead Thread instance, you’ll get a
runtime (not compiler) exception. A dead thread is not alive.
a thread goes into the New state when it has been created.
a thread goes into the Ready state when
it is started or
its time slice ends or
it calls yield() or
it is preempted or
the I/O event that it is waiting for occurs or
it is wakened by (notify() or notifyAll()) or
it is interrupted
a thread goes into the Running state when it is chosen by the scheduler from the
ready pool. comment: the ready pool is not necessarily a queue.
a thread goes into the Not Runnable state when
it is blocked for I/O or
it calls sleep() or
it calls join() or
it calls wait() or
it is waiting for a lock.
A thread goes into the Dead state (aka: Stop) when
its run method exits or
(its applet’s stop is called if it is in an applet). example: when control
leaves the page or
An exception occurs that is not handled within the thread.
a thread is Ready iff it is in the ready pool.
isAlive() purpose: to indicate a thread’s state. comment: this is the only such
method.
isAlive() returns true iff a thread’s state is (Ready or Running or Not Runnable).
comment: no way to tell which. comment: Ready or Running is sometimes called
Runnable.
4
isAlive() returns false if a thread’s state is New or Dead. comment: no way to tell
which.
if a thread’s state = Dead then it will soon disappear completely. similar: a
zombie process in Unix.
Thread Priority and Scheduling
Most computers have only one CPU hence threads must share the CPU with
other threads. | The execution of multiple threads on a single CPU is called
thread scheduling. | The Java Runtime System supports a very simple
scheduling algorithm (called: fixed priority scheduling).
thread priority –(
-- to change you call setPriority(int).
-- >= MIN_PRIORITY (= 1)
-- <= MAX_PRIORITY (= 10) comment: constants defined in the Thread class).
The scheduler –(
-- always runs higher priority threads before lower priority threads. comment: this
can cause starvation.
-- chooses threads of equal priority by using usually fair scheduling when the
running thread stops executing. comment: fair scheduling is not guaranteed.
if a thread (T1) whose priority is higher than the running thread (T2) enters the
ready pool then (T1 will immediately cause T2 to enter the ready pool and T1 will
start running). comment: T2 will not finish its time slice.
The Java Runtime System does not time slice.
The operating system implementation of threads that underlies the Java Thread
class may support time slicing. comment: all modern operating systems support
time slicing.
You should not write code that relies on time slicing if you want platform
independent code.
yield() can only yield to threads of the same priority. comment: if there are none it
is ignored. comment: sleep() will force other threads to run. comment: some texts
say yield() is not always guaranteed to work.
the following are instances of thread methods: start(), run(), yield(), wait(), join(),
interrupt(), sleep(), notify(), notifyAll().
Synchronizing
5
Synchronizing means: controlling time relationships.
Nearly all synchronizing involves a shared resource R, which in Java must be an object (a
piece of memory). Hence multiple threads will need to access R but must not at the same
time to avoid interference. Hence we need critical sections for R that will cause mutual
exclusion.
critical section (aka: CS) means: a piece of code that is guaranteed to not have
any other potentially conflicting code that is running concurrently with it.
mutual exclusion (aka: ME) means: preventing threads from creating invalid data
by running concurrently.
Suppose R is a point in 2D space with an x and a y coordinate. Suppose thread T1 starts
changing R and then thread T2 looks to see where R is. Suppose there is no
synchronizing. Then a time slice (thread switch) could happen after the x was changed
but before the y was, i.e. R is only “half moved”. Hence T2 would see an invalid R. This
is called a race condition (unpredictable result since we cannot predict when a time slice
will hit). So changing x and y must be noninterleavable, ie you must put in a critical
section, therefore if x is changed so is y. Thus once T1 starts changing R it will
completely finish before T2 gets a chance to see R. This is the whole idea in
synchronizing. Here is a simple CS:
begin CS
change x
change y
end CS
//
//
//
//
//
generic idea of a CS
no slicing here
ditto
until end, other threads cannot
get x and y
race condition means: a situation where time relationships between multiple
threads is not predictable hence data may be invalid.
You define a critical section in Java by declaring a (method or block)
synchronized. example:
synchronized move(int x, int y) {
this.x = this.x + x;
this.y = this.y + y;
}
synchronized is an instance of a keyword.
synchronized means: the associated (block or method) is a critical section.
Locks
6
Lock definition: a flag that belongs to an object. purpose: to control a critical
section for the object to prevent race conditions.
Every object has a lock.
Every lock has a wait set.
The wait set of a lock purpose: to hold threads waiting for the lock.
To wait on an object a thread must own the object’s lock. comment: this means
that the wait() will be inside synchronized code.
Primitive values (e.g. numbers) are not a kind of object; they must be wrapped
before they can be locked.
If a synchronized (method or block) is attempted on an object (R) and R’s lock is
available then (the code proceeds and the lock is set to notAvailable).
When a synchronized (block or method) exits, it sets the lock to available.
comment: what if this never happens?
If a synchronized (method or block) is attempted on an object (R) and R’s lock is
not available then the code waits until the lock is available.
If several (methods or blocks) are waiting on an object (R) and R’s lock becomes
available then one of them is chosen by the scheduler to proceed.
A lock belongs temporarily to a thread so the thread can call other synchronized
methods on this object without deadlocking.
a thread relinquishes a lock when it (exits a synchronized method or waits).
when a thread sleeps, it holds on to its locks.
The Java Runtime System allows a thread to re-acquire a lock that the thread
already holds.
reentrant means: allowing a thread to re-acquire a lock that the thread already
holds.
Java locks are reentrant.
Reentrant locks purpose: they eliminate the possibility of a single thread
deadlocking itself on a lock that it already holds.
Consider this class:
7
public class Reentrant {
public synchronized void a() {
b();
System.out.println("here I am, in a()");
}
public synchronized void b() {
System.out.println("here I am, in b()");
}
}
Reentrant contains two synchronized methods: a and b. The first
a, calls the other synchronized method, b.
synchronized method,
When control enters method a, the current thread acquires the lock for the Reentrant
object. Now, a calls b and because b is also synchronized the thread attempts to acquire
the same lock again. Because Java supports reentrant locks, this works. The current
thread can acquire the Reentrant object's lock again and both a and b execute to
conclusion as is evidenced by the output:
here I am, in b()
here I am, in a()
In systems that don't support reentrant locks, this sequence of method calls would cause
deadlock.
The Producer/Consumer Example (modified from Sun Java Tutorial)
Returning now to the critical section example, we just put synchronized on the method to
move a point and to read a point. This enforces mutual exclusion, so that both x and y
will be changed together without interruption. But it does not ensure that the read comes
after the move. That takes more machinery which we will now discuss.
First we show a simple example where one thread may have to wait;
public synchronized void moveToPoint(Point p) {
// moves a point to location p
self.x = p.x;
self.y = p.y;
notifyAll();
// wakes up all threads waiting on P
// NB on 1 cpu, only one will be chosen to execute next
// the others join the ready pool to execute soon
}
public synchronized void readPoint() {
try {
wait();
// wait is wakened by notifyAll,
}
catch (InterruptedException e) {
}// always good to print a meaningful message here
//code here to do something with self.x and self.y when
// awakened.
8
}
suppose thread T1 is the mover and T2 is the reader. The synchronized methods solve the
ME problem, and the wait forces T2 to wait for the move if it is running ahead of T1.
Here, R is the point object (p) on which these methods are called. NotifyAll tells all
threads waiting on R to execute the statement following the wait, which is normally to
check their conditions. But here there is no condition, it will go ahead regardless. If there
is a condition and it is false, the wait is normally reentered. That is why you usually see
waits in a loop. If we know there is only one waiter, or don’t care which one is wakened,
notify is more efficient. It nondeterministically chooses one waiting thread. If there are
several, the highest priority will always go first.
nondeterministic means: a choice is made in some unspecified manner.
comment: you must not care how. comment: it does not necessarily mean
random.
If threads have conditions (most do, eg producer/consumer) that control what happens
when they wake up, then some thread will set a condition that lets the right thread T
proceed when T wakes up and finds its condition has changed so it can exit its while-wait
loop and proceed. We will see this below in the producer/consumer example. The whilewait loop is not really “busy waiting” since it executes once on each wakeup and either
goes back to waiting or continues executing the wake up code. While it is waiting it is not
using the cpu.
If wait has a argument eg wait(1000) then the wait lasts until (a notify occurs or
the time expires or an interrupt occurs).
In the producer/consumer example, the put and get methods of the CubbyHole (the
shared R) are the critical sections. The Consumer should not access R when the Producer
is changing it, and the Producer should not modify it when the Consumer is getting the
value. So put and get in the CubbyHole class should be marked with the synchronized
keyword.
Here's a code skeleton for the CubbyHole class:
public class CubbyHole {
private int contents;
private boolean available = false; // says whether
// value in cubbyHole is available
public synchronized int get() {
...
}
public synchronized void put(int value) {
...
}
}
9
Note that the method declarations for both put and get contain the synchronized
keyword. Hence, the system associates a unique lock with every instance of CubbyHole
(including the one shared by the Producer and the Consumer).
Whenever control enters a synchronized method, the thread that called the
method locks the object whose method has been called.
Whenever control enters a synchronized method, other threads cannot call a
synchronized method on the same object until the object is unlocked.
An object is unlocked when a synchronized method (exits or waits.)
The lock, which is not user accessible, is attached to the cubbyHole object that will be
used (see below).
So, when the Producer calls CubbyHole's put method, it locks the CubbyHole, thereby
preventing the Consumer from calling the CubbyHole's get method:
public
//
//
//
}
synchronized void put(int value) {
CubbyHole locked by the Producer (beginning of CS)
put the value in the cubbyHole..
CubbyHole unlocked by the Producer (end of CS)
When the put method returns the CubbyHole is automatically unlocked.
Similarly, when the Consumer calls CubbyHole's get method, it locks the CubbyHole,
thereby preventing the Producer from calling put:
public
//
//
//
}
synchronized int get() {
CubbyHole locked by the Consumer (beginning of CS)
get the value from the cubbyHole
CubbyHole unlocked by the Consumer (end of CS)
The (acquisition and release) of a lock is done (automatically and atomically) by
the Java Runtime System.
Synchronization for ME isn't the whole story. We still have to control the timing so that
puts will alternate with gets and a put will be first, ie, the two threads must also be able
to tell one another when they've done their job. This is done using wait and notify
methods.
Using the notifyAll and wait Methods
The CubbyHole will store its value in a private instance variable called contents.
CubbyHole has another private instance variable, available, that is a boolean.
available is true when the value has just been put but not yet gotten and is false when
10
the value has been gotten but a new one has not yet been put. So, here's one possible
implementation for the put and get methods:
public synchronized int get() {
// won't work!
if (available == true) {
available = false;
return contents;
}
}
public synchronized void put(int value) {
// won't work!
if (available == false) {
available = true;
contents = value;
}
}
As implemented, these two methods won't work. Look at the get method. What happens
if the Producer hasn't put anything in the CubbyHole and available isn't true? get
does nothing. Similarly, if the Producer calls put before the Consumer got the value, put
doesn't do anything. What we really want is for the Consumer to wait until the Producer
puts something in the CubbyHole and the Producer must notify the Consumer when it's
done so. Similarly, the Producer must wait until the Consumer takes a value (and notifies
the Producer of its activities) before replacing it with a new value. The two threads must
coordinate more fully. We use Object's wait and notifyAll methods to do this. We
must explicitly specify when to wait, and when to proceed. (notify signals “proceed”).
Here are the new implementations of get and put that wait on and notify each other of
their activities:
public synchronized int get() {
while (available == false) {
try {
// wait for Producer to put value
wait();
} catch (InterruptedException e) {
}
}
available = false; // here when wait ends
// notify Producer that value has been retrieved
notifyAll();
return contents;
}
public synchronized void put(int value) {
while (available == true) {
try {
// wait for Consumer to get value
wait();
} catch (InterruptedException e) {
}
}
contents = value; // here when wait ends
available = true;
// notify Consumer that value has been set
notifyAll();
11
}
The code in the get method loops until the Producer has produced a new value. Each
time through the loop, get calls the wait method. The wait method relinquishes the lock
held by the Consumer on the CubbyHole (thereby allowing the Producer to get the lock
and update the CubbyHole) and then waits for notification from the Producer. When the
Producer puts something in the CubbyHole, it notifies the Consumer by calling
notifyAll. The Consumer then comes out of the wait state, the “condition” variable
called available is now true, so loop exits, and the get method returns the value in the
CubbyHole.
The put method works in a similar fashion, waiting for the Consumer thread to consume
the current value before allowing the Producer to produce a new one.
The notifyAll method wakes up all threads waiting on the object that has the lock (in
this case, the CubbyHole). The awakened threads, coming from the ready pool, compete
for the lock. One thread gets it, and the others go back to waiting. The Object class also
defines the notify method, which arbitrarily wakes up one of the threads waiting on this
object.
notify() is a method of the class Object.
notifyAll() is a method of the class Object.
wait() is a method of the class Object.
Wait Sets and Notification
Every object, in addition to having an associated lock, has an associated wait set, which is
a set of threads that are waiting for it. When an object is first created, its wait set is
empty. Wait sets are used by the methods wait, notify, and notifyAll of class Object.
These methods also interact with the scheduling mechanism for threads.
The method wait must be called for an object only when the current thread (call it T) has
already locked the object's lock i.e. inside a synchronized method. Suppose that thread T
has in fact performed N lock actions that have not been matched by unlock actions. The
wait method then adds the current thread to the wait set for the object, disables the
current thread for thread scheduling purposes, and performs N unlock actions to
relinquish the lock.
A thread T waits until (
some other thread invokes the notify() method for that object and thread T
happens to be the one arbitrarily chosen as the one to notify. or
some other thread invokes the notifyAll() method for that object. or
if the call by thread T to the wait method specified a timeout interval then
the specified amount of real time has elapsed. ).
12
The thread T is then removed from the wait set and reenters the ready pool for thread
scheduling. It then locks the object again (which may involve competing in the usual
manner with other threads); once it has gained control of the lock, it performs
additional lock actions and then returns from the invocation of the wait method. Thus, on
return from the wait method, the state of the object's lock is exactly as it was when the
wait method was invoked.
The notify method should be called for an object only when the current thread has
already locked the object's lock. If the wait set for the object is not empty, then some
arbitrarily chosen thread is removed from the wait set and re-enabled for thread
scheduling. (Of course, that thread will not be able to proceed until the current thread
relinquishes the object's lock.)
The notifyAll method should be called for an object only when the current thread has
already locked the object's lock. Every thread in the wait set for the object is removed
from the wait set and re-enabled for thread scheduling. (Of course, those threads will not
be able to proceed until the current thread relinquishes the object's lock.)
notify() is a method of the class Object.
notifyAll() is a method of the class Object.
wait() is a method of the class Object.
if a notify() occurs when the intended waiting thread is not waiting then it has no
effect.
Thread Methods
The following are Static Methods of the class Thread:
currentThread action: returns the Thread object which is the currently
running thread.
yield action: causes the Java Runtime System to context switch from the
current thread to the next available runnable thread.
sleep (int n) action: causes the Java Runtime System to put the current
thread to sleep for n milliseconds. | this thread will become eligible to run
again after n milliseconds have expired. comment: The clocks on most Java
runtimes will not be able to be accurate to less than 10 milliseconds.
The following are Instance Methods of the class Thread:
start action: tells the Java Runtime System to start a thread by entering
the run method. | run is the single method in the Runnable interface. | run
is called by the start method after the proper system thread has been
initialized.
13
stop (deprecated) action: causes this thread to stop immediately.
comment: This is often an abrupt way to end a thread, especially if this method is
executed on the current thread. In this case, the line immediately after the stop
method call is never executed because the thread context dies before stop returns.
A cleaner way to stop a thread is to set some variable which will cause the run
method to exit cleanly.
suspend (deprecated) action: causes a thread to stop running without
destroying the underlying system thread, or the state of the formerly
running thread. | If a thread is suspended, you can call resume on the
same thread to cause it to start running again.
resume (deprecated) action: revives a suspended thread. comment: There
is no guarantee that the thread will start running right away, since there might be a
higher-priority thread running already, but resume causes the thread to become
eligible for running.
setDaemon(true) action: makes this thread a daemon thread.
setPriority (int p) action: sets the thread's priority to the value p.
getPriority action: returns the thread's current priority, a value between
one and ten.
setName (String name) action: identifies the thread with a humanreadable name.
getName action: returns the current String value of the thread's name as
set by setName.
destroy action: kills a thread. | The Java Runtime System does not have
chance to intervene. | destroy is not recommended for routine use.
interrupt action: causes (sleep, wait or join) to end with an
InterruptedException.
wait action: causes a thread to wait until a later notifyAll or notify is made
wait(long timeout) action: waits for notification or until the timeout period
has elapsed. timeout is measured in milliseconds. \
wait(long timeout, int nanos) action: waits for notification or until timeout
milliseconds plus nanos nanoseconds have elapsed.
notifyAll action: wakes up all waiting threads in the wait set of an object
notify : action: wakes up at most one waiting thread.
join: action: lets a thread wait for another thread to complete at a later time
or, aThread.join(long msec) will limit the wait to msec.
The following are thread constructors:
Thread()
Thread(Runnable target)
Thread(Runnable target, String name)
Thread(String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, String name)
The following are conditions that might prevent a thread from running :
14
The thread is not the highest priority thread and so cannot get CPU time.
The thread has been put to sleep using the sleep() method
There is more than one thread with the same higher priority and JVM is switching
between these threads, at the moment, the thread in question is awaiting CPU
time.
The thread is waiting on a condition because some thread invoked wait() for the
thread.
The thread has explicitly yielded control by invoking yield()
It is blocked for file I/O
Static Methods Can Be Synchronized
Static methods can be synchronized.
Every class loaded in Java has a corresponding instance of java.lang.Class (called the
class object) that represents that class.
The class object is used as lock for class methods.
You get the class object how: anObject getClass().
synchronized static method. example: public static synchronized void getCount() {
}
Daemon Threads
daemon thread definition: a kind of thread that will be killed when no other
threads of the program are alive.
Threads In Applets
From:
http://www.javaranch.com/CodeBarn/BarnThreads2NoSleepApplet.jsp
HelloThreadTwoNoSleep
import java.applet.Applet ;
import java.awt.List ;
public class HelloThreadTwoNoSleep extends Applet implements Runnable
{
// declare instance variables
private Thread threadOne ;
private Thread threadTwo ;
private Thread threadThree ;
private List myList ;
public void init()
{
// init the thread variables
threadOne = new Thread( this );
threadTwo = new Thread( this );
threadThree = new Thread( this );
15
// now give each Thread a name
threadOne.setName( "Bert" );
threadTwo.setName( "Marley" );
threadThree.setName( "Lucky" );
// threads have been created, but are not yet "alive"
// create the list
myList = new List( "Thread info goes here" );
//set up the GUI (just the list)
add( myList );
//start the Threads
threadOne.start();
threadTwo.start();
threadThree.start();
// this causes the Threads to become runnable
// ALL of them will be running through this applet object's
// run method, but each Thread will be running at a different
// moment.
} // close init
public void run()
{
// this run method will repeat 10 times
for ( int i = 0 ; i < 10 ; i++ )
{
myList.addItem( Thread.currentThread().getName() + " is running" );
} // close for loop
myList.addItem( Thread.currentThread().getName() + " is done" );
} // close run
} // close applet
Explanation of HelloThreadTwoNoSleep applet
Number of classes: 1 (HelloThreadTwoNoSleep.class)
What it does: starts three threads; each thread will loop through the same run method 10
times and display its own name as it's running. You can use it to see the order in which
the three Thread objects actually run. Then compare this with the Sleep variation of
HelloThreadTwo, which puts each Thread to sleep in the run() method.
To run code as a thread requires two things:
1) a Thread object (this is the WORKER)
2) a Runnable object with a run method (the JOB for the WORKER)
The HelloThreadTwo.class (an applet subclass) implements the Runnable interface.
Run() is the only method required by classes which implement Runnable. The run()
method will be called by the Thread object.
16
The class which implements Runnable is the "target" of the Thread object.
So in this example, four different objects are used, three unique Thread objects/instances,
and one for the Runnable (target) of the Thread (which is the Applet itself). Using
different objects (for the Thread and the target Runnable) is the most common way of
having code run as a thread.
What actually happens in this applet:
Four instance variables are declared: three for the Thread objects, and one for the GUI
List (used to display the number for each loop the Thread object makes in the run()
method of the applet).
init() is called:
-creates and adds the List to the applet
-creates three new Thread objects
giving each one 'this' (the applet) as a target
-calls the Thread object's start() method
(which causes the Thread to call its target's run() method)
run() is called:
- has a for loop which loops 10 times and displays the Thread name
in the List.
What to look for: notice the order the Threads run in. Without being "forced" to sleep,
the Threads will not necessarily take turns. If you can run this applet on both a Macintosh
and a PC, you might see a DRASTIC difference. On a PC, thread scheduling often works
the way you might hope: the Threads appear to take turns, more or less. But on a
Macintosh, one Thread might just stay in until it has finished!
Miscellaneous
volatile is an instance of a keyword.
volatile purpose: to declare that a variable must be written to or read from
memory atomically to avoid a race condition that would result in invalid data.
you set a thread variable to null. purpose: the thread object can be garbage
collected.
you should not use (stop(), suspend(), resume()). reason: they cause subtle
problems.
17
a variable is set atomically unless it is of type (double or long).
if the operating system has round robin scheduling then the Java Runtime
System will probably use this scheduling.
the green thread model means: threads are scheduled by the Java Runtime
System.
the native thread model means: threads are scheduled by the operating system.
the green thread model is sometimes used on (Unix and Macintosh).
the native thread model is always used on Windows.
Java on Windows will effectively have only seven priority levels. why: because
Windows only has seven priority levels.
thread group purpose: to group a number of threads for control.
18