Cover | Table of Contents | Colophon
main()
.main() method of your application. When you
write a Java applet, there is a thread that is executing the callback
methods (init(),
actionPerformed(), etc.) of your applet; we speak
of this thread as the applet's thread. In either case, your
program starts with what you can consider as a single thread. If you
want to perform I/O (particularly if the I/O might block), start a
timer, or do any other task in parallel with the initial thread, you
must start a new thread to perform that task. In the next chapter,
we'll examine how to do just that.public class OurClass {
public void run() {
for (int I = 0; I < 100; I++) {
System.out.println("Hello");
}
}
}
run() that simply
writes a string 100 times to the Java console or to the standard
output. If we execute this code from an applet as shown here, it runs
in the applet's thread:import java.applet.Applet;
public class OurApplet extends Applet {
public void init() {
OurClass oc = new OurClass();
oc.run();
}
}
run() method, nothing unusual happens. An object
is created, its run() method is called, and the
"Hello" message prints 100 times. Just like other method
calls, the caller of the run() method waits
until the run() method finishes before it
continues. If we were to graph an execution of the code, it would
look like Figure 2.1.
public class OurClass {
public void run() {
for (int I = 0; I < 100; I++) {
System.out.println("Hello");
}
}
}
run() that simply
writes a string 100 times to the Java console or to the standard
output. If we execute this code from an applet as shown here, it runs
in the applet's thread:import java.applet.Applet;
public class OurApplet extends Applet {
public void init() {
OurClass oc = new OurClass();
oc.run();
}
}
run() method, nothing unusual happens. An object
is created, its run() method is called, and the
"Hello" message prints 100 times. Just like other method
calls, the caller of the run() method waits
until the run() method finishes before it
continues. If we were to graph an execution of the code, it would
look like Figure 2.1.
java.lang.Runnable), which is defined as
follows:public interface Runnable {
public abstract void run();
}
run() method. The Thread class actually
implements the Runnable interface; hence, when you inherit from the
Thread class, your subclass also implements the Runnable interface.
However, in this case we want to implement the Runnable interface
without actually inheriting from the Thread class. This is achieved
by simply substituting the phrase "implements Runnable"
for the phrase "extends Thread"; no other changes are
necessary in step 1 of our thread creation process:public class OurClass implements Runnable {
public void run() {
for (int I = 0; I < 100; I++) {
System.out.println("Hello, from another thread");
}
}
}
start() method to start a thread, and how to
terminate a thread by arranging for its run()
method to complete. We'll now look at two techniques that
provide us more information about the thread during its life cycle.start() method before the virtual machine
can actually start the thread. Similarly, when a thread returns from
its run() method, there is a period of time
before the virtual machine can clean up after the thread; and if you
use the stop() method, there is an even greater
period of time before the virtual machine can clean up after the
thread.run() method
returns, there is a short period of time before the thread stops. If
we want to know if the start() method of the
thread has been called—or, more usefully, if the thread has
terminated—we must use the isAlive()
method. This method is used to find out if a thread has actually been
started and has not yet terminated:
import java.applet.*;
import java.awt.*;
public class Animate extends Applet {
int count, lastcount;
Image pictures[];
TimerThread timer;
public void init() {
lastcount = 10; count = 0;
pictures = new Image[10];
MediaTracker tracker = new MediaTracker(this);
for (int a = 0; a < lastcount; a++) {
pictures[a] = getImage(
getCodeBase(), new Integer(a).toString()+".jpeg");
tracker.addImage(pictures[a], 0);
}
tracker.checkAll(true);
}
public void start() {
timer = new TimerThread(this, 1000);
timer.start();
}
public void stop() {
timer.shouldRun = false;
toString() method of the thread. The developer
who assigns the name is free to use this string for any purpose
desired. For example, let's assign a name to our TimerThread
class:import java.awt.*;
public class TimerThread extends Thread {
Component comp; // Component that needs repainting
int timediff; // Time between repaints of the component
volatile boolean shouldRun; // Set to false to stop thread
public TimerThread(Component comp, int timediff) {
this.comp = comp;
this.timediff = timediff;
shouldRun = true;
setName("TimerThread(" + timediff + " milliseconds)");
}
public void run() {
while (shouldRun) {
try {
comp.repaint();
sleep(timediff);
} catch (Exception e) {}
}
}
}
getName() method is later called on this
instance, this string value will be returned.currentThread()
method:currentThread()
method. The object returned is the same Thread object first created
for the current thread.public class AsyncReadSocket extends Thread {
StringBuffer result;
public AsyncReadSocket(String host, int port) {
// Open a socket to the given host.
}
public void run() {
// Read data from a socket into the result string buffer.
}
// Get the string already read from the socket so far.
// Only allows "Reader" threads to execute this method.
public String getResult() {
String reader = Thread.currentThread().getName();
if (reader.startsWith("Reader")) {
String retval = result.toString();
result = new StringBuffer();
return retval;
} else {
return "";
}
}
}
import java.applet.Applet;
public class Animate extends Applet {
TimerThread t;
public void start() {
if (t == null)
t = new TimerThread(this, 500);
t.start();
}
public void stop() {
t.shouldRun = false;
try {
t.join();
} catch (InterruptedException e) {}
// t = null;
}
}
start() method of the applet created a new
TimerThread object and started it. But what if we had only created
the TimerThread once? In the example just shown, we once again create
a new TimerThread in the start() method of the
applet; however, since we know the thread will be stopped in the
stop() method, we try to restart the stopped
thread in the start() method. In other words, we
create the TimerThread only once and use this one thread object to
start and stop the animation. By starting and stopping a single
TimerThread, we do not need to create a new instance of TimerThread
every time the applet is started, and the garbage collector will not
need to clean up the TimerThread instance that's left when the
applet is stopped and the TimerThread dereferenced.start() method,
nothing happens. The start() method won't
return an exception condition, but the run()
method also won't be called. The isAlive()
method also won't return true. In other
words, never restart a thread. An instance of a thread object should
be used once and only once.run() method a little further on, but it is
essentially an empty method.run() method
defined in this thread class.
public class AutomatedTellerMachine extends Teller {
public void withdraw(float amount) {
Account a = getAccount();
if (a.deduct(amount))
dispense(amount);
printReceipt();
}
}
public class Account {
private float total;
public boolean deduct(float t) {
if (t <= total) {
total -= t;
return true;
}
return false;
}
}
public class AutomatedTellerMachine extends Teller {
public void withdraw(float amount) {
Account a = getAccount();
if (a.deduct(amount))
dispense(amount);
printReceipt();
}
}
public class Account {
private float total;
public boolean deduct(float t) {
if (t <= total) {
total -= t;
return true;
}
return false;
}
}
getAccount(), dispense(),
and printReceipt() methods have already been
implemented. For our purposes, we are simply examining this algorithm
at a high level, so these methods will not be implemented here.available() method. However, not all input
streams support that method, and on a slow network, writing data to a
socket is also likely to take a long time. In general, checking for
data via the available() method is much less
efficient (and much harder to program) than creating a new thread to
read the data.result variable, or any
buffer at the same time, is to use the concept of a busy flag: if a
thread needs to access the result variable, it
must set the flag to busy. If the flag is already busy, the thread
must wait until the flag is free, at which point it must set the flag
to busy. The thread is then free to access the buffer without fear of
it being accessed by the other thread. Once the task is completed,
the thread must set the flag to not busy.
getBusyFlag() method is not declared as
synchronized. This is because it's not necessary:
getBusyFlag() does not try to access the
busyflag variable. Instead, it calls the
tryGetBusyFlag() method, which accesses the
busyflag and is, of course, synchronized.
Let's take another look at the
getBusyFlag() method, one that does not call the
tryGetBusyFlag() method. Instead, this version
gets the busyflag directly:public synchronized void getBusyFlag() {
while (true) {
if (busyflag == null) {
busyflag = Thread.currentThread();
break;
}
try {
Thread.sleep(100);
} catch (Exception e) {}
}
}
tryGetBusyFlag() method. In
our new version of the getBusyFlag() method, we
now access the busyflag directly. The
getBusyFlag() method simply loops waiting for
the flag to be freed, sets the flag, and returns. Since we are now
accessing the busyflag directly, we must make
the method synchronized or we will have a race condition.getBusyFlag() and
tryGetBusyFlag() methods from being run at the
same time (which prevents a race condition), it also prevents the
freeBusyFlag() method from running. This means
that if the flag is busy when getBusyFlag() is
called, getBusyFlag() waits until the flag is
freed. Unfortunately, since the freeBusyFlag()
method will not run until the getBusyFlag()
method frees the object lock, the busyflag will
not be freed. This Catch-22 situation is termed
deadlock. The deadlock in this case is a problem
between a lock and a busyflag. More commonly,
deadlock occurs between two or more locks, but the idea is the same.getBusyFlagOwner() method simply returns the
busyflag, which just so happens to be the thread
object that owns the lock. An implementation is as follows:public synchronized Thread getBusyFlagOwner() {
return busyflag;
}
freeBusyFlag() method to use this new
getBusyFlagOwner() method:public synchronized void freeBusyFlag () {
if (getBusyFlagOwner() == Thread.currentThread()) {
busyflag = null;
}
}
freeBusyFlag() method, we
make a call to the getBusyFlagOwner() method to
see if the current thread is the owner before freeing the
busyflag. What is interesting here is that both
the freeBusyFlag() and the
getBusyFlagOwner() methods are synchronized. So
what happens? Does the thread hang at the
getBusyFlagOwner() method while waiting for the
freeBusyFlag() method to free the object lock?
If not, and the getBusyFlagOwner() method is
allowed to run, what happens when that method completes? Does it free
the object lock even though the freeBusyFlag()
method still needs it? The answer to all these questions is that it
all works the way you want it to.getBusyFlag() method synchronized. The fact that
the freeBusyFlag() method was also synchronized
made it impossible for the busyflag to be freed
until the getBusyFlag() method returned. Since
the getBusyFlag() method was waiting for the
busyflag to be freed, it would wait forever.public void removeUseless(Folder file) {
synchronized (file) {
if (file.isUseless()) {
Cabinet directory = file.getCabinet();
synchronized (directory) {
directory.remove(file);
}
}
}
}
removeUseless(). This method is called during
the period when the program needs to clean up the database system. It
is passed a folder object; this object represents some folder we have
in our database system. There is some indication of uselessness that
is calculated by the isUseless()this reference. It is not possible
to obtain the object lock of an object that does not exist. So how
does synchronization of static methods work? To answer this question,
we will introduce the concept of a class lock. Just as there is an
object lock that can be obtained for each instance of a class
(object), there is a lock that can be obtained for each class. We
will refer to this as the class lock
. In terms of
implementation, there is no such thing as a class lock, but it is a
useful concept to help us understand how this all works.synchronized
keyword of the Java language. This keyword allows us to synchronize
methods and blocks of code.synchronized keyword, but they are useful in many
situations.public class BusyFlag {
protected Thread busyflag = null;
protected int busycount = 0;
public void getBusyFlag() {
while (tryGetBusyFlag() == false) {
try {
Thread.sleep(100);
} catch (Exception e) {}
}
}
public synchronized boolean tryGetBusyFlag() {
if (busyflag == null) {
busyflag = Thread.currentThread();
busycount = 1;
return true;
}
if (busyflag == Thread.currentThread()) {
busycount++;
return true;
}
return false;
}
public synchronized void freeBusyFlag () {
if (getBusyFlagOwner() == Thread.currentThread()) {
busycount--;
if (busycount == 0)
busyflag = null;
}
}
public synchronized Thread getBusyFlagOwner() {
return busyflag;
}
}public class BusyFlag {
protected Thread busyflag = null;
protected int busycount = 0;
public void getBusyFlag() {
while (tryGetBusyFlag() == false) {
try {
Thread.sleep(100);
} catch (Exception e) {}
}
}
public synchronized boolean tryGetBusyFlag() {
if (busyflag == null) {
busyflag = Thread.currentThread();
busycount = 1;
return true;
}
if (busyflag == Thread.currentThread()) {
busycount++;
return true;
}
return false;
}
public synchronized void freeBusyFlag () {
if (getBusyFlagOwner() == Thread.currentThread()) {
busycount--;
if (busycount == 0)
busyflag = null;
}
}
public synchronized Thread getBusyFlagOwner() {
return busyflag;
}
}
sleep() method. We originally used this method
to avoid eating up too many CPU cycles. At the time, we considered
this an open issue. If the getBusyFlag() method
sleeps for a long period of time, this might cause the method to wait
too long and hence cause a performance problem. Conversely, if the
method does not sleep enough, it might eat up too many CPU cycles and
hence cause a performance problem. In either case, this has to be
fixed: we have to find a way to wait only until the lock is freed. We
need the notifyAll()
method, which helps us in those cases where the program cannot be
designed to allow any arbitrary thread to receive the notification.
This method is similar to the notify() method,
except that all of the threads that are waiting on the object will be
notified instead of a single arbitrary thread. Just like the
notify() method, the
notifyAll() method does not let us decide which
threads get notification: they all get notified. By having all the
threads receive notification, it is now possible for us to work out a
mechanism for the threads to choose among th