Advanced Swing Multithreading Tutorial

A lot of Swing tutorials on the internet focus on SwingWorkers and dealing
with a single thread running concurrently to the Event Dispatch Thread (EDT). This tutorial is going
to look at some more tricky techniques and traps that one might encounter
when dealing with numerous threads updating the GUI.

In this tutorial we will assume that we want a table that displays a number
of rows. Behind each of these rows a process should exist which will be updated by
some background thread, and we want to reflect these updates in the table. In
effect we are dealing with x-number of threads running in the background and
updating x-number of cells in a JTable.

 

screen example :

 

progressTable

 


Part 1. Create the Swing Application First

Firstly lets assume we have some simple bean called Process. This entity will characterise the process that we will be dealing with.

Process.java.

We want this process to have its progress value updated by some
background thread. In our example we want our process updated in random increments to 100.

Now lets create a table model for our JTable, which will display these process entities.

ProcessTableModel.java.

There is nothing unusual about this table model. We are creating 200
instances of Process and adding them to the internal arraylist for this table
model. The internal arraylist in this code example is shown as external because we would like to have this table model as an inner class.

Our renderer for the progress column is going to look a bit more interesting.
We are going to use a JProgressBar to render the 0 to 100 value inside each
Process bean. Since table renderers can return JComponents this is a
perfectly acceptable way of rendering a value.

ProcessProgressRenderer.java:

Lets create a basic GUI where we can put this table into, along with a button
which will fire off all the processes displayed in the table.

ThreadGUITest.java :

Part 2. Start Creating the Multithreading Aspect

Now lets get the thread magic happening. After the use clicks the ‘start all’ button,
each process will be fired off. The process itself will not actually be fired off (it is not a thread), however a wrapper class will handle the threading for the process entity. (Note : it is important not to make entities threads. Delegate this responsibility to a facade or proxy)

After the thread is fired off, the progress value will be re calculated.

ProgressUpdator.java

The fireTableCellUpdated(row, 1) method on the table model, notifies the
event dispatch thread to re-render that particular cell in the JTable. It is possible to use the method updateTableModel() from the table model class, which would update every cell in the JTable, however this would be more performance intensive and unnecessary in our case.

Each one of these ProgressUpdaters (threads per se) will access one of the process
elements in the arraylist, hence the reason we are passing a row number into
the constructor. The row number is what links the ProgressUpdater with the Process.

Let’s add an Actionlistener to the button. On clicking the ‘start-all’ button all threads
will be fired off.

Run the GUI. Pretty cool huh? But there is something missing. When all the
processes have finished the start all button still remains disabled. We would
like to know when all the processes have finished, so that we can re-enable the button.

We could use
Thread.join()
. The thread join() method joins the calling thread to the main thread, effectively freezing the main thread until all joined threads
have run to completion. We could join all the threads in the
actionPerformed(..) method, and once they have run to completion we could set
the button to enabled again.

A valid assumption would be to change the actionPerformed method like this :

But now when we run our program, everything freezes. Whats going on? Why
can’t we fire off all our threads in the actionPerformed method and have them
all joined within this method? Once the threads have finished running,
why can’t we then update any GUI code?

The reason is that we are blocking the event dispatch thread. Nothing can
happen to the GUI while all the threads are running to completion. They are
not joined to the main thread, rather they are joined to the EDT.

 

 

 

 

We need to solve this by having all the threads joined on another thread. All
we have to do is take out all the thread code in the actionPerformed(..)
method and fire off that thread, leaving the EDT alone.

Create a thread and put the code in it like this :

Now we can fire off this thread in the actionPerformed(..) method. After completion of all the processes the button will be enabled again!

Hope you enjoy and please leave a comment.

Full source code :

 

 

 


Posted in Swing, Tutorial | Tagged , , , , , , , | 3 Comments

3 Responses to Advanced Swing Multithreading Tutorial

  1. joe mann says:

    Tried compiling the source code with java version “1.6.0_27″
    got the following errors:

    ThreadGUITest.java:76: inconvertible types
    found : java.util.ArrayList
    required: ThreadGUITest.ProgressUpdater
    for (ProgressUpdater pu : (ThreadGUITest.ProgressUpdater) progressUpdaters) {
    ^
    ThreadGUITest.java:90: incompatible types
    found : java.lang.Object
    required: java.lang.Thread
    for (Thread pu : ts) {
    ^
    ThreadGUITest.java:123: incompatible types
    found : java.lang.Object
    required: ThreadGUITest.Process
    final Process pr = processList.get(row);
    ^
    ThreadGUITest.java:197: incompatible types
    found : java.lang.Object
    required: ThreadGUITest.Process
    return processList.get(row2);
    ^
    ThreadGUITest.java:214: incompatible types
    found : java.lang.Object
    required: ThreadGUITest.Process
    Process p = processList.get(row);
    ^
    Note: ThreadGUITest.java uses unchecked or unsafe operations.
    Note: Recompile with -Xlint:unchecked for details.
    5 errors

  2. Tom says:

    I know it’s an old post but this code is bad. The renderer extends DefaultTableCellRemderer. That class is itself a JLabel (ie a JComponent) and is intended to be used directly. Furthermore you are creating a new JProgressBar every time the cell is rendered.

    Calling fireTableCellUpdated will cause JTable.tableChanged to be entered. This is not thread-safe.

Leave a Reply to felixzacat Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

THREE_COLUMN_PAGE