Concepts / Blocking Operations and the GUI Thread

Blocking Operations and the GUI Thread

ConnectionHandler is a subclass of Thread that opens the network connection and reads incoming messages, keeping blocking network work off the GUI thread.

  • Programming

Why Network Work Gets Its Own Thread

The Asynchronous Network Chat Program uses a nested class named ConnectionHandler to handle most of its network-related work. ConnectionHandler is a subclass of Thread, and it has two jobs: opening the network connection, and then reading incoming messages once that connection exists. Understanding why this class is a thread, and not just an ordinary helper, is the key to reading the rest of the program.

Before you read any code, try a quick prediction. The Listen button creates a ConnectionHandler with port 5000. Does the constructor itself open a server socket on port 5000? Decide your answer before you continue.

What do you think happens?

When the Listen button creates a ConnectionHandler with port 5000, does the constructor open a server socket on port 5000?

  • Yes, the constructor opens the server socket immediately

  • No, the constructor only stores settings and starts the thread

Reveal answer
Answer:

No. The constructor only sets instance variables, posts a status message, and starts the thread. The socket is created later, inside run().

Keeping the constructor short means the button click returns quickly. The slow work of opening the socket happens on the new thread, where it cannot freeze the window.

The Problem a Blocking Read Creates

Opening a connection is a blocking operation. It can take some time to complete. Reading incoming messages is also blocking: the program waits until data arrives. If that waiting happened on the same thread as the window, the program would be stuck waiting, and the window would stop responding to the user while it waited.

GUI threadruns the blocking readWindowstops respondingGUI threadkeeps answering the userConnectionHandlerthreadwaits on the networkblockedhands off the read
What happens to the window when a blocking read runs on the GUI thread, compared with running it on a separate ConnectionHandler thread?

The design divides the labor cleanly. The GUI thread is responsible for responding to the user. The ConnectionHandler thread is responsible for waiting on the network. Placing the connection-opening code in its own thread means the GUI is not blocked while the connection is being opened. When you are debugging, most questions reduce to one question: which of these two threads does this line of code run on?

Two Constructors for Two Roles

The thread is created when the user clicks either the Listen button or the Connect button, and each button maps onto its own constructor. The Listen button makes the thread act as a server, using ConnectionHandler(int port). The Connect button makes the thread act as a client, using ConnectionHandler(String remoteHost, int port). The class has one constructor for each role.

Listen buttonserver modeConnectionHandler(intport)stores the portState LISTENINGset in the constructorConnect buttonclient modeConnectionHandler(StringremoteHost, int port)stores host and portState CONNECTINGset in the constructorcreatessetscreatessets
How do the server-mode and client-mode constructors differ in what they set up, and what does each leave for later?
ConstructorWhat it doesWhat it does not do
ConnectionHandler(int port), server modeSets the state to LISTENING, stores the port, posts a status message to the transcript area, and starts the threadDoes not open a ServerSocket; that happens later in run()
ConnectionHandler(String remoteHost, int port), client modeSets the state to CONNECTING, stores the remote host and the port, posts a status message, and starts the threadDoes not open a Socket to the remote host; that happens later in run()

Both constructors only set up fields and start the thread. The network work is deferred to run().

Tracing Execution from Constructor to run()

Now trace the full order of execution. The constructor and start() run on the thread that created the object, which is the GUI thread. Calling start() is what makes the new thread run: once started, the thread executes run(). Everything from run() onward runs on the ConnectionHandler thread. The flowchart below shows this boundary, which is the handoff from the GUI's work to the network's work.

Constructorsets fields, posts statusstart()thread is startedrun()server or clientOpen connectionServerSocket or SocketconnectionOpened()creates BufferedReader inReading loopwhile state is CONNECTEDin.readLine()one line per passreceived(input)message reportedfinally: cleanUp()runs before the thread endsconnectionClosedFromOtherSide()when the result is nullsame threadnew thread beginssets upconnection readyenter loopstate CONNECTEDnon-nullnullnext passthen cleanuploop exits
In what order do the constructor, start(), run(), the reading loop, and the finally block execute, and which of them run on the new thread?

Worked Trace: A Listen Click

Worked example

Following one Listen click through the program

The user clicks Listen, which creates ConnectionHandler with port 5000. Trace where each step runs and when the server socket is opened.

  1. 1
    Constructor on the GUI thread

    The server constructor sets the state to LISTENING, stores port 5000, and posts a status message to the transcript area. It does not open a server socket.

  2. 2
    start() on the GUI thread

    start() makes the new thread run. The GUI thread is free to keep responding to the user.

  3. 3
    run() on the ConnectionHandler thread

    run() checks the state and sees the server role. It creates a ServerSocket on port 5000, accepts one connection, and closes the listener.

  4. 4
    connectionOpened()

    This sets up the connection, including creating the BufferedReader named in.

  5. 5
    Reading loop

    While the state is CONNECTED, each pass calls in.readLine(). A non-null line goes to received(input). A null result triggers connectionClosedFromOtherSide().

  6. 6
    finally block

    Whether the loop ended normally or an exception occurred, cleanUp() runs before the thread terminates.

Result

The socket was opened on the ConnectionHandler thread, not during the button click. The window stayed responsive while the thread waited for a connection.

Mistakes Beginners Make When Reading This Code

  • Common mistake

    Assuming the constructor opens the socket

    What it looks like

    Expecting a server socket on port 5000 to exist as soon as the Listen button is clicked.

    Why it fails

    The constructor only sets instance variables, posts a status message, and starts the thread.

    Do this instead

    Look in run() for the ServerSocket or Socket creation.

  • Common mistake

    Thinking start() runs run() on the current thread

    What it looks like

    Believing run() executes inline, so it would block the button handler.

    Why it fails

    Calling start() is what makes the new thread run, and the thread then executes run().

    Do this instead

    Treat everything from run() onward as running on the ConnectionHandler thread.

  • Common mistake

    Expecting every exception to appear in the transcript

    What it looks like

    Closing the connection on purpose and then wondering why no error appears.

    Why it fails

    The catch block reports errors only if the state is not CLOSED.

    Do this instead

    Check the state before deciding an error was lost.

  • Common mistake

    Confusing in.readLine() returning null with an error; null means the other side closed the connection

Locating the Code for a Symptom

When something goes wrong, first ask which thread the suspicious line runs on. Then use the structure you have just traced to narrow the search. The table maps common symptoms to the part of the design most likely to be involved, based on the stages described in the source.

SymptomWhere to look firstWhy
Window freezes while connectingAny blocking call that runs on the GUI thread, such as code in the constructor or start() pathOpening a connection and reading are blocking; they belong on the ConnectionHandler thread
Connection never appears to openThe server or client branch in run() and connectionOpened()The socket and BufferedReader are created in these stages
Incoming message does not showThe reading loop, in.readLine() and received(input)Each non-null line is passed to received(input)
Peer disconnect shown unexpectedlyThe null check and connectionClosedFromOtherSide()A null read means the other side closed the connection
Missing error messageThe catch block and its check for the CLOSED stateErrors are reported only when the state is not CLOSED
Resource left open after failureThe finally block and cleanUp()This is the single place where teardown happens

Use the thread boundary and the run() stages to choose the first place to inspect.

MEDIUM

A window freezes right after the user clicks Connect. Using the thread boundary, explain which thread should be doing the network work, which constructor was involved, and which method you would inspect first. Then explain why the error might be hidden if the connection was closed on purpose.

Hints
  • Ask which thread runs the constructor and which runs run().
  • The client constructor takes a remote host and a port.
  • Check the state before deciding whether an error should be reported.

Key Takeaways

  • ConnectionHandler is a Thread subclass that opens the network connection and reads incoming messages, keeping blocking network work off the GUI thread.
  • The Listen button uses ConnectionHandler(int port) and the Connect button uses ConnectionHandler(String remoteHost, int port); both constructors only set fields, post a status message, and start the thread.
  • start() makes the new thread run run(), which opens the ServerSocket or Socket, calls connectionOpened(), loops on in.readLine() while the state is CONNECTED, and finally calls cleanUp().
  • Errors reach the user through the catch block only when the state is not CLOSED, and the finally block is the single place where teardown happens.
  • When debugging, ask which thread a line runs on: the GUI thread responds to the user, and the ConnectionHandler thread waits on the network.