Concepts / Swing Event-Handling Thread

Swing Event-Handling Thread

A Swing Timer's thread only generates events; the handler code that responds to them runs on the Swing event-handling thread, so using a timer is not the same as running animation on a separate thread.

  • Programming

A Timer Looks Like a Thread

A common first assumption when building an animation in a Swing application is that a timer is simply a convenient kind of thread. The reasoning goes like this: if you can write an animation loop that runs on its own thread, then a Swing Timer must do the same job with less code. The two approaches look similar on the surface, but they place your animation logic in different threads, and that difference determines whether your program is safe.

What do you think happens?

When a Swing Timer fires, which thread runs the code that responds to the event?

  • The timer's own thread

  • The Swing event-handling thread

  • A brand new thread created for each tick

Reveal answer
Answer:

The Swing event-handling thread

The timer's thread only generates events at regular intervals. The handler code that responds to those events executes on the Swing event-handling thread, which is the same thread that handles repainting and user actions such as mouse clicks and key presses.

The reason this matters is that Swing does not protect its components and their state variables from being touched by several threads at once. Once you understand where your animation code actually runs, the potential for trouble becomes easy to see. The rest of this article follows the path of a timer event step by step, and then shows what goes wrong when the path is broken.

Tracing a Timer Event Through Its Threads

Think of the Swing Timer as a source of ticks. Its underlying thread does one job: it generates events at regular intervals. It does not draw anything, and it does not update your animation state. Each event is then handled by code that executes on the Swing event-handling thread. That same thread is responsible for repainting components and for responding to user actions such as mouse clicks and key presses.

Timer threadgenerates ticks atintervalsSwing event-handlingthreadhandles painting and useractionsHandler coderesponds to each tickdelivers timer eventexecutes handler
When the timer fires, which thread runs the handler code, and how does the event get from the timer's thread to that handler?

The diagram shows the key point. The timer's thread stops after producing the event. The handler is then executed on the Swing event-handling thread, so any code that changes animation variables and any code that paints the frame end up on that same thread. As long as everything involving components and their variables happens there, the system behaves predictably.

Where a Separate Animation Thread Breaks the Rule

Trouble starts when another thread reaches into the same components or the same variables. A separate thread that changes animation state while the GUI thread is reading that state is the situation to watch for. Consider an animation class in which one method, updateForNextFrame(), changes several variables that describe the current frame, such as a position and a size. Another method, paintComponent(), reads those same variables in order to draw the frame.

Suppose the animation logic calls updateForNextFrame() from a thread other than the Swing event-handling thread, while paintComponent() is running on the Swing event-handling thread. The two methods can then overlap in time. Nothing in the code has to look wrong for this to happen. The overlap is simply a consequence of two threads running at once against shared data.

How a Race Condition Produces a Mixed Frame

The overlap described above is a race condition. If paintComponent() reads the variables while updateForNextFrame() is partway through changing them, it may see a mixture of values. Some would belong to the new frame and some to the previous frame. The drawn picture would then be assembled from inconsistent data, even though each individual assignment was correct on its own.

Update sets positionnew frame valuePaint reads positiongets new positionUpdate sets sizestill in progressPaint reads sizegets old sizeinterleavesthenthen
If a background thread changes a variable while paintComponent() is reading it, how can the two threads interleave so the painted frame shows an inconsistent state?
Worked example

Tracing a Mixed Frame

An animation stores a position and a size for the current frame. A background thread calls updateForNextFrame() to move the shape to the next frame while the Swing event-handling thread runs paintComponent(). What can be drawn?

  1. 1
    Step 1: The update thread writes the new position

    The position variable now holds the value for the new frame. The size variable still holds the value for the previous frame.

  2. 2
    Step 2: The paint thread reads the position

    paintComponent() reads the new position, which is correct for the new frame.

  3. 3
    Step 3: The update thread is still changing variables

    updateForNextFrame() has not yet written the new size. It is part way through its work.

  4. 4
    Step 4: The paint thread reads the size

    paintComponent() now reads the old size, which belongs to the previous frame. The shape is drawn with a new position and an old size.

Result

The frame is drawn from a mixture of values. Each assignment was correct on its own, but the painted picture is inconsistent.

The source gives two ways to protect against this. The first is to keep GUI state changes on the Swing event-handling thread. The second is to synchronize access to any shared variables that painting depends on. Either approach ensures that painting never reads variables in the middle of an update.

Why repaint() Is the Exception

Not every GUI call carries the same risk. The repaint() method of a component can be called safely from any thread, without worrying about synchronization. This is worth remembering because it is a common place where beginners worry unnecessarily. The reason is that repaint() does not paint anything itself. It only asks the system to schedule a paint event. The actual painting is carried out later, on the Swing event-handling thread.

Animation threadcalls repaint()Paint event scheduledrequest onlySwing event-handlingthreadcarries out the eventpaintComponent()draws the framescheduleshanded toruns later
When a non-GUI thread calls repaint(), what actually happens, and where does the actual painting take place?

So an animation thread can request a repaint freely. It should not, however, perform the drawing itself or change the variables that the drawing depends on without coordination. The safe call and the unsafe work are separate operations, and only the call is safe from any thread.

Comparing a Thread Loop With a Swing Timer

The two approaches differ in which thread does the work and which thread touches the GUI. The table below summarizes the structural differences described in the source.

QuestionAnimation loop on its own threadSwing Timer
Which thread generates the timing?The animation thread's own loopThe timer's underlying thread
Which thread runs the animation logic?The separate animation threadThe Swing event-handling thread
Which thread paints the frame?The Swing event-handling thread, reached from outsideThe Swing event-handling thread
Main riskRace conditions when the loop changes variables used during paintingLower, as long as GUI work stays on the event-handling thread

Structural differences between a separate animation thread and a Swing Timer

The table makes the misconception visible. Both approaches produce a repeating animation, but only the timer keeps the animation logic on the thread that Swing expects. The extra code of a timer is not a shortcut around threading. It is a way of placing the work where it is safe.

The source also notes that Java includes another timer class, java.util.Timer, which is suited to non-GUI programs. The choice depends on context. A Swing Timer is designed to work with the Swing event-handling thread, which makes it a natural fit for GUI animation. A general-purpose timer suits work that has no graphical components to protect.

Mistakes Beginners Make With Swing Threads

  • Common mistake

    Assuming a Swing Timer runs the animation code on its own thread

    Why it fails

    The timer's thread only generates events. The handler code that responds to them runs on the Swing event-handling thread.

    Do this instead

    Read the handler as event-thread code, and keep GUI state changes there.

  • Common mistake

    Updating animation variables from a background thread while painting reads them

    What it looks like

    A loop on a separate thread calls updateForNextFrame() while paintComponent() is drawing the same frame.

    Why it fails

    The painting code can read a mixture of old and new values, producing a frame built from inconsistent data.

    Do this instead

    Keep the updates on the Swing event-handling thread, or synchronize access to the shared variables that painting depends on.

  • Common mistake

    Avoiding repaint() from background threads out of fear

    Why it fails

    repaint() can be called safely from any thread because it only schedules a paint event.

    Do this instead

    Call repaint() freely, but do not let the background thread perform the drawing or change painting variables without coordination.

  • Common mistake

    Using java.util.Timer for a GUI animation without considering the event-handling thread

    Why it fails

    java.util.Timer is designed for non-GUI programs and is not tied to the Swing event-handling thread.

    Do this instead

    Use a Swing Timer when the work touches Swing components, and reserve the general-purpose timer for work that has no graphical components to protect.

Checking Your Understanding of a Shared Variable

MEDIUM

An animation uses a Swing Timer. Its handler method changes the x position and then the y position of a shape. The paintComponent() method draws the shape using x and y. Is this code at risk of a race condition, and why? Explain which thread runs each piece.

Hints
  • Ask which thread runs the timer's handler.
  • Ask which thread runs paintComponent().
  • Consider whether both pieces of code are on the same thread.
EASY

A background thread is used to calculate the next frame, and it calls repaint() after each calculation. List which operations are safe from that thread and which must be coordinated.

Hints
  • repaint() only schedules a paint event.
  • The variables that paintComponent() reads are the ones needing care.

Key Takeaways

  • A Swing Timer's thread only generates events. The handler code that responds to them runs on the Swing event-handling thread.
  • Using a Swing Timer is not the same as running animation on a separate thread, because the two place the animation logic on different threads.
  • A background thread that changes variables used by paintComponent() can cause a race condition, so the painted frame may mix values from different frames.
  • repaint() is safe to call from any thread because it only schedules a paint event, which is carried out later on the Swing event-handling thread.