Threads and Simple Animation
Everything so far has redrawn PaintApplet in response to something the user did - a drag, a
key press, a button click. True animation needs something different: the applet redrawing itself
continuously, on its own schedule, whether or not the user is doing anything. That means running a loop
on a background Thread.
The Classic Pattern: Runnable + start()/stop()
The applet implements Runnable, giving it a run() method that becomes the body
of the animation loop. A Thread wrapping this is created and started inside the
applet's own start() lifecycle method, and stopped inside stop():
| Lifecycle method | What it does for the animation |
|---|---|
start() | Creates and starts the background Thread |
run() | Loops: move the ball, call repaint(), sleep a bit, repeat |
stop() | Signals the loop to end cleanly, via a flag - never by force-killing the thread |
Why Not Thread.stop()?
Old Java code sometimes calls the long-removed Thread.stop() method to kill a thread
immediately. It's unsafe by design: it can interrupt the thread at literally any point, potentially
leaving shared objects in a half-updated, inconsistent state, and it has been deprecated for that reason
since very early Java and removed from modern JDKs entirely. The safe, standard pattern instead is a
volatile boolean running flag that the loop checks on every iteration, and that stop()
simply sets to false - the thread notices on its own next loop check and exits normally,
never mid-operation.
Example: BouncingBallApplet
A small standalone example - not a change to PaintApplet - showing the whole pattern: a ball
that moves a few pixels per frame and reverses direction when it hits an edge.
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
public class BouncingBallApplet extends Applet implements Runnable {
private volatile boolean running = false;
private Thread animationThread;
private int x = 20, y = 20;
private int dx = 3, dy = 2;
private static final int SIZE = 20;
public void start() {
running = true;
animationThread = new Thread(this);
animationThread.start();
}
public void stop() {
// Ask the loop to end - never force-kill it with the removed Thread.stop()
running = false;
}
public void run() {
while (running) {
x += dx;
y += dy;
if (x <= 0 || x + SIZE >= getWidth()) dx = -dx;
if (y <= 0 || y + SIZE >= getHeight()) dy = -dy;
repaint();
try {
Thread.sleep(30); // ~33 frames per second
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
running = false;
}
}
}
public void paint(Graphics g) {
g.setColor(Color.ORANGE);
g.fillOval(x, y, SIZE, SIZE);
}
}
Why Thread.sleep() needs a try/catch: sleep() declares a checked exception,
InterruptedException, that fires if another thread interrupts this one while it's sleeping.
You can't just ignore it - the code above re-sets the interrupt status with Thread.currentThread().interrupt()
(good practice so the interruption isn't silently swallowed) and also flips running to
false, ending the loop on the next check rather than looping forever with a broken sleep.
Why This, Historically
A production-quality Swing application written today would typically use a javax.swing.Timer
instead of a raw background thread for this kind of animation - it fires its callback on the
Event Dispatch Thread automatically, sidestepping thread-safety concerns with Swing components entirely.
Raw threads are what's traditionally taught alongside applets, though, since Applet predates
javax.swing.Timer's common use and the Runnable/Thread pattern above
is what you'll actually find in most applet-era code and course material.