Applets are embeddable Java applications that are expected to start and stop themselves on command, possibly many times in their lifetime. A Java-enabled web browser normally starts an applet when the applet is displayed and stops it when the user moves to another page or (in theory) when the user scrolls the applet out of view. To conform to this API, we would like an applet to cease its nonessential activity when it is stopped and resume it when started again. We’ll talk about applets in Chapter 23, but it’s not really essential to know about them here. We’ll just use this as a more realistic example and as a transition to talk about our next topic, synchronization.
In this section, we will build UpdateApplet
, a simple base class for an applet
that maintains a thread to automatically update its display at regular
intervals. UpdateApplet
handles the
basic creation and termination of the thread in the Applet’s start()
and stop()
methods:
public
class
UpdateApplet
extends
java
.
applet
.
Applet
implements
Runnable
{
Thread
thread
;
boolean
running
;
int
updateInterval
=
1000
;
public
void
run
()
{
while
(
running
)
{
repaint
();
try
{
Thread
.
sleep
(
updateInterval
);
}
catch
(
InterruptedException
e
)
{
System
.
out
.
println
(
"interrupted..."
);
return
;
}
}
}
public
void
start
()
{
System
.
out
.
println
(
"starting..."
);
if
(
!
running
)
// naive approach
{
running
=
true
;
thread
=
new
Thread
(
this
);
thread
.
start
();
}
}
public
void
stop
()
{
System
.
out
.
println
(
"stopping..."
);
thread
.
interrupt
();
running
=
false
;
}
}
UpdateApplet
is a Runnable
object that
alternately sleeps and calls its repaint()
method. (There’s nothing to paint,
though, so running this applet is kind of boring. Later we’ll subclass it
to implement a digital clock.) It has two other public methods: start()
and stop()
. These are methods of the Applet
class we are
overriding; don’t confuse them with the similarly named methods of the
Thread
class. These start()
and stop()
methods are called by the web browser or
applet viewer to tell the applet when it should and should not be
running.
UpdateApplet
illustrates an
environmentally friendly way to deal with threads in a simple applet.
UpdateApplet
simply dismisses its
thread each time the applet is stopped and recreates it if the applet is
restarted. When UpdateApplet
’s start()
method is called, we first check to make
sure there is no currently running thread by checking the running
flag. We then create one to begin our
execution. When our applet is subsequently asked to stop, we set the flag
indicating that it should stop and make sure the thread is awake by
invoking its interrupt()
method. In
this way, we are sure to catch the thread either at the beginning of its
next iteration or when it goes to sleep.
With UpdateApplet
doing all the
work for us, we can create the world’s simplest clock applet with just a
few lines of code. Figure 9-3 shows our
Clock
.
Here’s the code:
//file: Clock.java
public
class
Clock
extends
UpdateApplet
{
public
void
paint
(
java
.
awt
.
Graphics
g
)
{
g
.
drawString
(
new
java
.
util
.
Date
().
toString
(),
10
,
25
);
}
}
The java.util.Date().toString()
method creates a string that contains the current time.
Our applet seems pretty straightforward and, in fact, works as
advertised. But some things in it should concern us when we’re thinking
about threads. Let’s look at that quick check of the running
flag before we
start our new thread:
if
(
!
running
)
// naive approach
{
running
=
true
;
...
/* start thread */
Now, an Applet
’s start()
and stop()
methods are guaranteed to be called in
sequence and probably by the same controlling thread. As a result, this
check for the existence of the running thread in start()
may not seem
necessary here. The stop()
method should
always be called before the start()
method is invoked again. But, in the style of defensive programming the
test seems like a good thing to do, right? That may be so, but, in
general, it’s not enough to prevent bad things from happening. The test
may prevent a simple case of misaligned stop()
and start()
calls, but the bigger question lurking
here is, What happens if start()
and
stop()
were called repeatedly or in
very quick succession in a multithreaded environment? In the extreme
case, it would be possible for two threads to enter the test at about
the same time and there is the chance that we could end up with multiple
threads started and out of our control. What is needed is a real way to
gain exclusive access to a resource (our flag) for a period of time.
That’s what synchronization is all about, and we’ll cover it in detail
in the next section and throughout the rest of this chapter.
With synchronization, we might also consider more complex scenarios for our applet, such as keeping our thread alive but dormant while the applet is stopped. This would allow us to preserve expensive setup like network connections and clean them up later if necessary.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.