Java Threads
Java Threads
Guest Lecture
By
Avinash Gautam, CSIS, BITS Pilani
Contents
1. What is a thread ?
2. Define and launch a thread
3. The life-cycle of a thread
4. interrupt a thread
5. thread synchronization
6. other issues
What is a thread ?
A sequential (or single-threaded) program is one that, when
executed, has only one single flow of control.
i.e., at any time instant, there is at most only one instruction (or statement or
execution point) that is being executed in the program.
A multi-threaded program is one that can have multiple flows of
control when executed.
At some time instance, there may exist multiple instructions or execution
points) that are being executed in the program
Ex: in a Web browser we may do the following tasks at the same time:
1. scroll a page,
2. download an applet or image,
3. play sound,
4 print a page.
A thread is a single sequential flow of control within a program.
single-threaded vs multithreaded programs
{ A();
{ A(); A1(); A2(); A3(); newThreads {
B1(); B2(); } { A1(); A2(); A3() };
{B1(); B2() }
}
}
Thread ecology in a java program
started by B thread
lifetime of C thread
2. Define and launch a java thread
Each Java Run time thread is encapsulated in a java.lang.Thread
instance.
Two ways to define a thread:
1. Extend the Thread class
2. Implement the Runnable interface :
package java.lang;
public interface Runnable { public void run() ; }
Steps for extending the Thread class:
1. Subclass the Thread class;
2. Override the default Thread method run(), which is the entry point of the
thread, like the main(String[]) method in a java program.
Define a thread
// Example:
public class Print2Console extends Thread {
public void run() { // run() is to a thread what main() is to a java program
for (int b = -128; b < 128; b++) out.println(b); }
… // additional methods, fields …
}
Impement the Runnable interface if you need a parent class:
// by extending JTextArea we can reuse all existing code of JTextArea
public class Print2GUI extend JTextArea implement Runnable {
public void run() {
for (int b = -128; b < 128; b++) append( Integer.toString(b) + “\n” ); }
}
How to launch a thread
1. create an instance of [ a subclass of ] of Thread, say thread.
1. Thread thread = new Print2Console();
2. Thread thread = new Thread( new Print2GUI( .. ) );
2. call its start() method, thread.start();. // note: not call run() !!
Ex:
Printer2Console t1 = new Print2Console(); // t1 is a thread instance !
t1.start() ; // this will start a new thread, which begins its execution by
calling t1.run()
… // parent thread continue immediately here without waiting for the child
thread to complete its execution. cf: t1.run();
Print2GUI jtext = new Print2GUI();
Thread t2 = new Thread( jtext);
t2.start();
…
The java.lang.Thread constructors
// Public Constructors
Thread([ ThreadGroup group,] [ Runnable target, ]
[ String name ] );
Instances :
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);
// name is a string used to identify the thread instance
// group is the thread group to which this thread belongs.
Some thread property access methods
int getID() // every thread has a unique ID, since jdk1.5
String getName(); setName(String)
// get/set the name of the thread
ThreadGroup getThreadGroup();
int getPriority() ; setPriority(int) // thread has priority in [0, 31]
Thread.State getState() // return current state of this thread
boolean isAlive()
Tests if this thread has been started and has not yet died. .
boolean isDaemon()
Tests if this thread is a daemon thread.
boolean isInterrupted()
Tests whether this thread has been interrupted.
State methods for current thread accesses
start() resume()
new Thread(…)
not-running thread t terminates
(ready) sleep done
o.notify(), o.notifyAll()
interrupt()
(set bit) interrupt()
yield(), or
preempty scheduled
by OS (throw exception)
by OS
o.wait()
sleep(…)
running t.join()
stop(),
terminated suspend()
run() exits
blocked by lock
normally or
abnormally
State transition methods for Thread
public synchronized native void start() {
start a thread by calling its run() method … Note: When we call
t.join(), we in fact use
It is illegal to start a thread more than once } current thread's time to
public final void join( [long ms [, int ns]]); execute code of t thread
Let current thread wait for receiver thread to die for at most ms+ns time
static void yield() // callable by current thread only
Causes the currently executing thread object to temporarily pause and
allow other threads to execute.
public final void resume(); // deprecated
public final void suspend();// deprecatedmay lead to deadlock
public final void stop(); // deprecated lead to inconsistency
// state checking
public boolean isAlive() ; // true if runnable or blocked
4. interrupting threads
A blocking/waiting call (sleep(),wait() or join()) to a thread t can be terminated by
an InterruptedException thrown by invoking t.interrupt().
this provides an alternative way to leave the blocked state.
however, the control flow is different from the normal case.
Ex: public void run() {
try { … while (more work to do) { // Normal sleep() exit continue here
do some work;
sleep( … ); // give another thread a chance to work
}
}
catch (InterruptedException e) { // if waked-up by interrupt() then continue here
… // thread interrupted during sleep or wait }
}
Note: the interrupt() method will not throw an InterruptedException if
the thread is not blocked/waiting. In such case the thread needs to
call the static interrupted() method to find out if it was recently
interrupted. So we should rewrite the while loop by
while ( ! interrupted() && moreWorkToDo() ) { … }
interrupt-related methods
void interrupt()
send an Interrupt request to a thread.
the “interrupted” status of the thread is set to true.
if the thread is blocked by sleep(), wait() or join(), the interrupted status of
the thread is cleared and an InterruptedException is thrown.
conclusion: runnable ==> “interrupted” bit set but no Exception thrown.
not runnable ==> Exception thrown but “interrupted” bit not set
static boolean interrupted() // destructive query
Tests whether the current thread (self) has been interrupted.
reset the “interrupted” status to false.
boolean isInterrupted() // non-destructive query
Tests whether this thread has been interrupted without changing the
“interrupted” status.
may be used to query current executing thread or another non-executing
thread. e.g. if( t1.isInterrupted() | Thread.currentThread()...) …
5. Thread synchronization
Problem with any multithreaded Java program :
Two or more Thread objects access the same pieces of data.
too little or no synchronization ==> there is inconsistency, loss or
corruption of data.
too much synchronization ==> deadlock or system frozen.
In between there is unfair processing where several threads can
starve another one hogging all resources between themselves.
Multithreading may incur inconsistency : an Example
Two concurrent deposits of 50 into an account with 0 initial balance.:
void deposit(int amount) {
int x = account.getBalance();
x += amount;
account.setBalance(x); }
deposit(50) : // deposit 1
x = account.getBalance() //1 deposit(50) : // deposit 2
x += 50; //2 x = account.getBalance() //4
account.setBalance(x) //3 x += 50; //5
The execution sequence: account.setBalance(x) //6
1,4,2,5,3,6 will result in unwanted result !!
Final balance is 50 instead of 100!!
Synchronized methods and statements
multithreading can lead to racing hazards where different orders of
interleaving produce different results of computation.
Order of interleaving is generally unpredictable and is not determined by the
programmer.
Java’s synchronized method (as well as synchronized statement)
can prevent its body from being interleaved by relevant methods.
synchronized( obj ) { … } // synchronized statement with obj as lock
synchronized … m(… ) {… } //synchronized method with this as lock
When one thread executes (the body of) a synchronized method/statement,
all other threads are excluded from executing any synchronized method with
the same object as lock.
Synchronizing threads
Java use the monitor concept to achieve mutual exclusion and
synchronization between threads.
Synchronized methods /statements guarantee mutual exclusion.
Mutual exclusion may cause a thread to be unable to complete its task. So
monitor allow a thread to wait until state change and then continue its work.
wait(), notify() and notifyAll() control the synchronization of threads.
Allow one thread to wait for a condition (logical state) and another to set it
and then notify waiting threads.
condition variables => instance boolean variables
wait => wait();
notifying => notify(); notifyAll();
Typical usage
synchronized void doWhenCondition() {
while ( !condition )
wait(); // wait until someone notifies us of changes in condition
… // do what needs to be done when condition is true
}
synchronized void changeCondition {
// change some values used in condition test
notify(); // Let waiting threads know something changed
}
Note: A method may serve both roles; it may need some condition to
occur to do something and its action my cause condition to change.
Java's Monitor Model
• A monitor is a collection of code (called the critical section) associated with an
object (called the lock) such that at any time instant only one thread at most can
has its execution point located in the critical section associated with the
lock(mutual exclusion).
• Java allows any object to be the lock of a monitor.
• The critical section of a monitor controlled by an object e [of class C ] comprises
the following sections of code:
• The body of all synchronized methods m() callable by e, that is, all synchronized methods
m(…) defined in C or super classes of C.
• The body of all synchronized statements with e as target:
synchronized(e) { … }. // critical section is determined by the lock object e
A thread enters the critical section of a monitor by invoking e.m() or executing a
synchronized statement. However, before it can run the method/statement, it
must first own the lock e and will need to wait until the lock is free if it cannot get
the lock. A thread owing a lock will release the lock automatically once it exit the
critical section.
Java's Monitor model (cintinued)
A thread executing in a monitor may encounter condition in which it
cannot continue but still does not want to exit. In such case, it can
call the method e.wait() to enter the waiting list of the monitor.
A thread entering waiting list will release the lock so that other
outside threads have chance to get the lock.
A thread changing the monitor state should call e.notify() or
e.notifyAll() to have one or all threads in the waiting list to compete
with other outside threads for getting the lock to continue execution.
Note: A static method m() in class C can also be synchronized. In
such case it belongs to the monitor whose lock object is C.class.
Java's monitor model (continued)
synchronized(e) { B4 }
e.m1() {B1}
e.wait() // cannot continue and
don't want return
Monitor controlled
by an object e
e.m2() {B2} With critical Waiting list
section for Threads
B1 U B2…U B5
e.m3(){B3} e.notify|notifyAll()
// notified by current monitor
executor if it changes state
synchronized(e) { B5 }
• Note since a section of code may belong to multiple monitors, it is possible that
two threads reside at the same code region belonging to two different
monitors..
Producer/Consumer Problem
Two threads: producer and consumer, one monitor: CubbyHole
The Producer :
generates a pair of integers between 0 and 9 (inclusive), stores it in a
CubbyHole object, and prints the sum of each generated pair.
sleeps for a random amount of time between 0 and 100 milliseconds before
repeating the number generating cycle:
The Consumer,
consumes all pairs of integers from the CubbyHole as quickly as they
become available.
Producer.java
public class Producer extends Thread {
private CubbyHole cubbyhole; private int id;
public Producer(CubbyHole c, int id) {
cubbyhole = c; this.id = id; }
public void run() {
for (int i = 0; i < 10; i++)
for(int j =0; j < 10; j++ ) {
cubbyhole.put(i, j);
System.out.println("Producer #" + this.id + " put: ("+i +","+j + ").");
try { sleep((int)(Math.random() * 100)); }
catch (InterruptedException e) { }
};
}
}
Consumer.java
public class Consumer extends Thread {
private CubbyHole cubbyhole;
private int id;
currentGroup.enumerate(listOfThreads);
for (int i = 0; i < numThreads; i++)
System.out.println("Thread #" + i + " = " +
listOfThreads[i].getName());
}
}
Methods that Operate on the ThreadGroup
getMaxPriority(), setMaxPriority(int)
isDaemon(), setDaemon(boolean)
A Daemon thread group is one that destroys itself when its last thread/group
is destroyed.
getName() // name of the thread
getParent() and parentOf(ThreadGroup) // boolean
toString()
activeCount(), activeGroupCount()
// # of active descendent threads, and groups