Transcript chap13

13: Concurrency
• Definition: A thread is a single sequential flow of control within a program.
• Some texts use the name lightweight process instead of thread.
• A thread is similar to a real process in that a thread and a running program
are both a single sequential flow of control.
• However, a thread runs within the context of a full-blown program and takes
advantage of the resources allocated for that program and the program's
environment.
• As a sequential flow of control, a thread must carve out some of its own
resources within a running program. (It must have its own execution stack
and program counter for example.)
Using the Timer and TimerTask Classes
import java.util.Timer;
import java.util.TimerTask;
public class Reminder {
Timer timer;
public Reminder(int seconds) {
timer = new Timer();
timer.schedule(new RemindTask(), seconds*1000);
}
class RemindTask extends TimerTask {
public void run() {
System.out.println("Time's up!");
timer.cancel(); //Terminate the timer thread
}
}
public static void main(String args[]) {
System.out.println("About to schedule task.");
new Reminder(5);
System.out.println("Task scheduled.");
}
}
Performing a Task Repeatedly
import java.util.Timer;
import java.util.TimerTask;
public class Reminder2 {
Timer timer;
public Reminder2(int seconds) {
timer = new Timer();
timer.schedule(new RemindTask(), 0, seconds*1000);
}
class RemindTask extends TimerTask {
public void run() {
System.out.println("Time's up!");
}
}
public static void main(String args[]) {
System.out.println("About to schedule task.");
Reminder2 p=new Reminder2(3);
System.out.println("Task scheduled.");
try{
Thread.sleep(10*1000);
}catch(Exception e){}
p.timer.cancel(); //Terminate the timer thread
}
}
Timer Thread
Main Thread
Customizing a Thread's run Method
• There are two ways for creating a new thread:
– Subclassing Thread and Overriding method run()
– Implementing the Runnable Interface
• To trigger the execution of new threads, call
the start() method in the Thread class.
The Life Cycle of a Thread
Subclassing Thread and Overriding run
class SimpleThread extends Thread {
public SimpleThread(String str) {
super(str);
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {}
} System.out.println("DONE! " + getName());
}}
public class TwoThreadsDemo {
public static void main (String[] args) {
new SimpleThread("Jamaica").start();
new SimpleThread("Fiji").start();
}}
0 Jamaica
0 Fiji
1 Fiji
1 Jamaica
2 Jamaica
2 Fiji
3 Fiji
3 Jamaica
4 Jamaica
4 Fiji
5 Jamaica
5 Fiji
6 Fiji
6 Jamaica
7 Jamaica
7 Fiji
8 Fiji
9 Fiji
8 Jamaica
DONE! Fiji
9 Jamaica
DONE! Jamaica
Implementing the Runnable Interface
public class RunnableThread implements Runnable {
private int countDown = 5;
public String toString() { return "#" +
Thread.currentThread().getName() + ": " + countDown;
}
public void run() {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
}
}
public static void main(String[] args) {
for(int i = 1; i <= 5; i++)
new Thread(new RunnableThread(), "" + i).start();
// Output is like SimpleThread.java
}
} ///:~
Implementing the Runnable Interface
A Runnable interface simply has a run( ) method,—it doesn’t
produce any innate threading abilities, like those of a class
inherited from Thread.
So to produce a thread from a Runnable object, you must create a
separate Thread object, handing the Runnable object to the
special Thread constructor.
Rule of Thumb: If your class must subclass some other class
(the most common example being Applet), you should use
Runnable.
The Life Cycle of a Thread
• Creating a Thread
– clockThread = new Thread(this, "Clock");
• Starting a Thread
– clockThread.start();
• Making a Thread Not Runnable .A thread becomes Not Runnable when
one of these events occurs:
– Its sleep method is invoked.
– The thread calls the wait method to wait for a specific condition to be
satisifed.
– The thread is blocking on I/O.
The Life Cycle of a Thread
• Stopping a Thread
– A thread arranges for its own death by having a run method
that terminates naturally.
public void run() {
Thread myThread = Thread.currentThread();
while (clockThread == myThread) {
…….}
}
public void stop() {clockThread = null; }
• The isAlive() Method
– The isAlive() method returns true if the thread has been started and
not stopped.
– If the isAlive() method returns false, you know that the thread either
is a New Thread or is Dead.
– If the isAlive() method returns true, you know that the thread is
either Runnable or Not Runnable.
Yielding
you can use yield method to tell the thread scheduling mechanism that you’ve done
enough and that some other thread might get the CPU.
public class YieldingThread extends Thread {
private int countDown = 5;
private static int threadCount = 0;
public YieldingThread() {
super("" + ++threadCount);
start();
}
public String toString() {
return "#" + getName() + ": " + countDown;
}
public void run() {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
yield();
}
}
Daemon threads
A “daemon” thread is one that is supposed to provide a general service in the
background as long as the program is running, but is not part of the essence of
the program. Thus, when all of the non-daemon threads complete, the program
is terminated.
public class SimpleDaemons extends Thread {
public SimpleDaemons() {
setDaemon(true);
// Must be called before start()
start();
}
public void run() {
while(true) {
try { sleep(100); }
catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(this);
}
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) new SimpleDaemons();
} } ///:~
Sharing limited resources
public class AlwaysEven implements Runnable{
private int i;
public void next() {
i++; i++; //do sth else
}
public int getValue() { return i; }
public void run() {
while(true) {
int val = getValue();
if(val % 2 != 0) {
System.out.println(val);
System.exit(0);
}
}
}
public static void main(String[] args) {
final AlwaysEven ae = new AlwaysEven();
(new Thread(ae) ).start();
while(true)
ae.next();
}
} ///:~
Sharing limited resources
• This example shows a fundamental problem with using
threads. You never know when a thread might be run.
– Imagine sitting at a table with a fork, about to spear the
last piece of food on your plate, and as your fork reaches
for it, the food suddenly vanishes (because your thread
was suspended and another thread came in and stole the
food).
• Preventing this kind of collision is simply a matter of
putting a lock on a resource when one thread is using it.
– The first thread that accesses a resource locks it, and then
the other threads cannot access that resource until it is
unlocked,.
Resolving shared resource contention
• Java has built-in support to prevent collisions
over resources in the form of the
synchronized keyword.
synchronized void f() { /* ... */ }
synchronized void g(){ /* ... */ }
• Each object contains a single lock.
– When you call any synchronized method, that
object is locked and no other synchronized
method of that object can be called by other
thread until the first one finishes and releases the
lock.
Resolving shared resource contention
Thread-1
Thread-2
synchronized
synchronized
Resolving shared resource contention
public class AlwaysEven2 implements Runnable{
private int i;
public synchronized void next() { i++; i++; }
public synchronized int getValue() { return i; }
public void run() {
while(true) {
int val = getValue();
if(val % 2 != 0) {
System.out.println(val);
System.exit(0);
}
}
}
public static void main(String[] args) {
final AlwaysEven2 ae = new AlwaysEven2();
(new Thread(ae) ).start();
while(true)
ae.next();
}
} ///:~
Critical sections
• Sometimes, you only want to prevent multiple
thread access to part of the code inside a method
instead of the entire method.
synchronized(syncObject) {
// This code can be accessed
// by only one thread at a time
}
• This is also called a synchronized block; before it
can be entered, the lock must be acquired on
syncObject.
– If some other thread already has this lock, then the
critical section cannot be entered until the lock is given
up.
Resolving shared resource contention
public class AlwaysEven2 implements Runnable{
private int i;
public void next() {
synchronized(this) { i++; i++; }
}
public int getValue() { synchronized(this){ return i; }
}
public void run() {
while(true) {
int val = getValue();
if(val % 2 != 0) {
System.out.println(val);
System.exit(0); }
}
}
public static void main(String[] args) {
final AlwaysEven2 ae = new AlwaysEven2();
(new Thread(ae) ).start();
while(true) ae.next();
}
} ///:~
Cooperation between threads
• In order to make threads cooperate with each other, Java
provides mechanism of handshaking between threads:
– Implemented using the Object methods wait( ) and notify( ) /
notifyAll( ).
• Typically, wait( ) is used when you’re waiting for some
condition to change (typically, this condition will be
changed by another thread).
• You may just idly wait while testing the condition inside
your thread; ( called a “busy wait” and it’s a very bad use of
CPU cycles. )
• wait( ) allows you to put the thread to sleep while waiting
for the condition to change, and only when a notify( ) or
notifyAll( ) occurs does the thread wake up and check for
changes. Thus, wait( ) provides a way to synchronize
activities between threads.
Producer-consumer Model
Thread A
Thread B
Product
Consumer
Producer
Storehouse
Cooperation between threads(1)
public class ProducerConsumer extends Object {
private Object slot;
public ProducerConsumer() {
slot = null; // null indicates empty
}
public synchronized void putIn(Object obj)
throws InterruptedException {
while ( slot != null ) {
wait();
}
slot = obj; // put object into slot
notifyAll(); // signal that slot has been filled
}
public synchronized Object takeOut()
throws InterruptedException {
while ( slot == null ) {
wait(); // wait while slot is empty
}
Object obj = slot;
slot = null; // mark slot as empty
notifyAll(); // signal that slot is empty
return obj;
}
Cooperation between threads
Wait and notify / notifyAll
• wait( ) allows you to put the thread to sleep while
waiting for some condition .
• only when a notify( ) or notifyAll( ) occurs does the
thread wake up and check for changes.
• the only place you can call wait( ), notify( ), or
notifyAll( ) is within a synchronized method
• It’s important to understand that sleep( ) does not
release the lock when it is called. On the other hand,
the method wait( ) does release the lock
• When a thread enters a call to wait( ) inside a
method, that thread’s execution is suspended, and
the lock on that object is released.
Cooperation between threads
• There are two forms of wait( ). The first takes an argument in
milliseconds that has the same meaning as in sleep( ):
– The object lock is released during the wait( ).
– You can come out of the wait( ) due to a notify( ) or notifyAll( ), or
by letting the clock run out.
• The second form of wait( ) takes no arguments; this version is more
commonly used.
– This wait( ) continues indefinitely until the thread receives a notify( )
or notifyAll( ).
• One fairly unique aspect of wait( ), notify( ), and notifyAll( ) is that
these methods are part of the base class Object and not part of Thread,
as is sleep( ).
• Although this seems a bit strange at first, it’s essential because they
manipulate the lock that’s also part of every object.
• As a result, you can put a wait( ) inside any synchronized method,
regardless of whether that class extends Thread or implements
Runnable.
Cooperation between threads
You can ask another object to perform an
operation that manipulates its own lock.
– For example, if you want to notify( ) an object x,
you must do so inside a synchronized block that
acquires the lock for x:
synchronized(x)
{
……
x.notify();
}
Cooperation between threads
class CubbyHole {
private int contents;
private boolean available = false;
public synchronized int get() {
while (available == false) {
try {
wait();
} catch (InterruptedException e
){}
}
//available now, available=true
//we get the content
//and reset the flag
available = false;
notifyAll();
return contents;
}
public synchronized void put(int value) {
while (available == true) {
try {
wait();
} catch (InterruptedException e
){}
}
//empty now, available=false
//we set the content
//and set the flag
contents = value;
available = true;
notifyAll();
}
}
Cooperation between threads(2)
class Consumer extends Thread {
private CubbyHole cubbyhole;
public Consumer(CubbyHole c) {
cubbyhole = c;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyhole.get();
System.out.println("Consumer #" + " got: " + value);
}
}
}
Cooperation between threads(2)
class Producer extends Thread {
private CubbyHole cubbyhole;
public Producer(CubbyHole c) {
cubbyhole = c;
}
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("Producer #" + " put: " + i);
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}
Cooperation between threads
public class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c);
Consumer c1 = new Consumer(c);
p1.start();
c1.start();
}
}
Exercises
1.
2.
Create an example of a “busy wait.” One thread sleeps
for awhile and then sets a flag to true. The second thread
watches that flag inside a while loop (this is the “busy
wait”) and when the flag becomes true, sets it back to
false and reports the change to the console.
Create a second version of the above exercise program
that uses wait( ) instead of the “busy wait” flag .
Cooperation between threads(1)
public class ProducerConsumer extends Object {
private Object slot;
public ProducerConsumer() {
slot = null; // null indicates empty
}
public synchronized void putIn(Object obj)
throws InterruptedException {
while ( slot != null ) {
wait();
}
public static void main(String[] args) {
final ProducerConsumer ch = new ProducerC
onsumer();
Runnable runA = new Runnable() {
public void run() {
try {
String str;
Thread.sleep(500);
slot = obj; // put object into slot
notifyAll(); // signal that slot has been filled
str = "multithreaded";
ch.putIn(str);
str = "programming";
ch.putIn(str);
}
public synchronized Object takeOut()
throws InterruptedException {
while ( slot == null ) {
wait(); // wait while slot is empty
}
Object obj = slot;
slot = null; // mark slot as empty
notifyAll(); // signal that slot is empty
return obj;
}
str = "with Java";
ch.putIn(str);
} catch ( InterruptedException x ) {
x.printStackTrace();
}
}
};
Cooperation between threads(1)
Runnable runB = new Runnable() {
public void run() {
try {
Object obj;
obj = ch.takeOut();
System.out.println("in run() - just took out: '" +
obj + "'");
Thread.sleep(500);
obj = ch.takeOut();
System.out.println("in run() - just took out: '" +
obj + "'");
obj = ch.takeOut();
System.out.println("in run() - just took out: '" +
obj + "'");
} catch ( InterruptedException x ) {
x.printStackTrace();
}
}
};
Thread threadA = new Thread(runA, "threadA");
threadA.start();
Thread threadB = new Thread(runB, "threadB");
threadB.start();
}
}