Thread that Thing!

One of my pet hates with graphical desktop programs is when developers use a single-threading model to mix UI-update-logic with grunt-logic. I see this a lot in small freeware utility programs, which is somewhat understandable (but not really). More worryingly though, I see it in large commercial products like Internet Explorer and Lotus Notes, which is most definitely unacceptable!

Problem Synopsis: A graphical desktop application can be broken down into two distinct modules: the interface module and the grunt module. The interface module is responsible for drawing the graphical interface through which the user can provide input via mouse/keyboard events and for showing visual feedback based on these events or events triggered from the grunt module. The grunt module is responsible for performing the actual grunt work required to implement the overall application’s functionality (the low-level stuff like sending/receiving bytes through sockets to display a web page in a browser, or scanning a hard-drive for files and parsing their content to find specific strings, etc).

The graphical interface module is the only thing that an end-user can see and interact with. This should thus always be highly responsive in showing feedback (think download progress bar) and in accepting input from the user (think user clicking to cancel download). When developers use a single-threaded model, they are merging these two modules together and mixing grunt logic with interface update logic. The result of this is that the grunt logic can have a severe impact on the interface responsiveness and completely cripple or ‘freeze’ an application.

Imagine what would happen if the download example above was being performed in a single thread: user initiates a download (interface module), download progress window comes up (interface module), application retrieves bytes through sockets (grunt module), download progress window displays percentage complete (interface module), download progress window aborts download when user clicks cancel button (interface module). In a single thread, all these tasks need to be executed sequentially one at a time from start-to-end. This means that once the download starts and the bytes are being received and written to disk, the interface module can’t actually update the progress bar as the thread is busy. The interface module can’t process the user’s cancel download click either, again because the thread is busy. If this was a large download that took say ten minutes, than the user would have completely no control over the download window for ten minutes and would see no visual progress updates for ten minutes!

Luckily no browser that I know of actually does this, they all use separate threads for the interface and grunt modules to show download progress and allow a user to cancel the download. This is how all graphical applications should be coded in all cases where the grunt module can run a task that could otherwise hang the interface for longer than say two or three seconds.

Unfortunately this is not the case. Developers are sometimes lazy (or inexperienced) and decide to do it all in the one thread (usually the case with small freeware tools), or they sometimes think there’s no need to use a multi-threaded model because the grunt work only takes one or two seconds at most and isn’t really noticeable. This second case is especially dangerous when an application scales or is used in unexpected ways forcing the grunt work to take significantly longer than expected.

This happens a lot in Lotus Notes when working directly with a remote replica. If your internet connection is fast, the user interfaces appears reasonably responsive and you can do most tasks without issues. However, when the connection degrades to a crawl, so does everything you do in the interface. Scrolling up in your email inbox can take over 30 seconds, or when clicking the refresh button the whole application appears to freeze.

The Solution: Simple: non-trivial grunt logic should always be performed in a separate thread from user-interface event-processing logic. This will allow both threads to be executed in parallel such that even if the grunt module is busy for extended periods of time, the interface module will still update the display and accept user input.

New developers usually find threading concepts difficult to grasp due to data synchronization issues, dead-locking, etc, but it’s really not that hard. All modern object-oriented languages have well established and well documented threading semantics that once learned are easy to implement and greatly enhance the usability of graphical applications. Learn to use them!

Threading in Java: I’m not going to delve into details on how to do multi-threaded programming in Java as there are hundreds of good tutorials readily available online. When writing a multi-threaded application using Swing, there are two constraints to keep in mind:
  • Time-consuming tasks should not be run on the Event Dispatch Thread. Otherwise the application may become unresponsive.
  • Swing components should be accessed on the Event Dispatch Thread only. Otherwise the application may behave unexpectedly. 
A few pointers to help abide by the above are:
  • SwingUtilities.invokeLater() is your friend. Use this in your grunt threads to wrap any logic that will cause an interface update and have it executed in the Event Dispatcher. Failure to do this will result in graphical glitches, unexpected behavior and uncaught exceptions.
  • SwingWorker (now part of Java 6) can be your friend. This utility class has a number of methods to help you run your grunt work in a background thread. I must admit to not having used this much to date as it was not part of the standard API, but it seems very handy. 
  • Local and Anonymous classes are useful to understand. Overusing these can lead to bad design and hard to maintain code, but using them well will lead to small, efficient coding. Have a play around with how to implement your own background threads from basic principles.
  • Thread.interrupt() and Thread.isInterrupted() are essential for allowing users to cancel lengthy processes. Contrary to initial instinct, Thread.interrupt() doesn’t actually interrupt a running thread! Rather, it just sets the interrupted flag which you should be polling for in loop iterations or catching InterruptedThreadException in blocking functions and handling appropriately. The key gotcha to be aware of is that Thread.isInterrupted() will clear the interrupted flag. Careful when using this in recursive functions.
  • Synchronization is critical when using threading. Learn what it means to have synchronized functions, synchronized static functions and synchronized blocks on specific objects. Getting this wrong can lead to unexpected behavior, data corruption, concurrent access exceptions and even dead-locks.

Comments

Popular posts from this blog

Wkhtmltopdf font and sizing issues

Import Google Contacts to Nokia PC Suite

Can't delete last blank page from Word