Controlling Threads

Author - Viraj Shetty

Introduction

One of the useful features in java is the built in support for writing multi-threaded applications. A thread, sometimes referred to as a lightweight process, is an execution path in the program which has it's own local variables, program counter and lifetime. If the task which is being executed on the thread takes a long time, then there needs to be a mechanism to

This article will take a non-trivial example without using threads and re-factor the code to include monitor, pause, resume and cancel capabilities. Finally, we will re-factor the code to develop a generic thread code to handle all these capabilities. This article assumes a basic understanding of threads and knowledge of SWING programming.

The Problem

Develop an application to search for all java source files, which contains a particular string token - within a particular directory recursively. Include a swing user interface from which the user will have the capability to choose a root directory from the local machine and enter the string token to be searched. The user will have the ability to start, stop, monitor, pause or resume the Search task. The final user interface should look as follows.

As seen in the figure, there are two fields called * Root Directory, which the user chooses from a JFileChooser * Token, which the user enters There are four buttons at the bottom, which correspond to searching, canceling, pausing and resuming the task. A status label next to the Resume button displays the percentage of work done. The output files are displayed at the center. All fields on the screen should be enabled only when required.

Note that we will solve this problem in 4 iterations and then re-factor the code to develop a generic worker thread, which handles pause, resume, stop and progress. The four iterations are as follows

Simplest Solution (No User Threads)

The simplest solution is to not use threads at all. There will be only one button on the screen called Search. The most common sense approach to solving this problem is to separate the display issues from the problem of finding the files. So, we will have two source files

The screen will look as follows.

File Finder.java

This class is responsible for finding all files under the root directory and which contains the string token. The constructor takes in the File object for the root directory. The findFiles(..) method returns the list of File objects.

The usage of the FileFinder class would look as follows

We are not going to spend time looking at the algorithm. I would encourage you to look at the complete source for each iteration before proceeding with the next iteration. Click here to see the complete source file for FileFinder.java.

SearchForm.java

SearchForm class will present a swing interface to the user and display the output when the user clicks on the search button. The constructor starts by setting the size of the window. It adds all the SWING components to the JFrame contentPane. In order to handle the button clicks, SearchForm implements an ActionListener interface. Listeners are common mechanism in SWING to handle the user actions from the screen.

The SearchForm.actionPerformed(..) method implements the functionality when either the Search or the Directory button is clicked. When the directory button is clicked, then a JFileChooser is displayed where a directory can be chosen as below

Note that the text displayed on the button changes when a directory is selected from the JfileChooser (This may not be the best implementation, but suffices)

When the Search button is clicked, the search needs to be invoked. In other words, the FileFinder object needs to be created and used as below.

This code makes sure that a directory is chosen before the search button is clicked. It uses the FileFinder object to find the files. The setTextArea(..) method is called to populate the JTextArea with the results. A snippet of the class is shown below.

Click here to see the complete source file for SearchForm.java.

Note that by separating the two classes, the view is separated from the model. This implementation works if the response time is small and the user is willing to wait. But if the directory contains many sub-directories and many java files, the amount of time taken to respond to a search request will be too much for the user to bear. Particularly, if this functionality was embedded in an editor, then the user would not be able to edit files if the application forces the user to wait till the search is completed.

Click here to see the complete list of source files for this iteration.

Adding a Thread with stop functionality

The solution to the above problem is clearly to use threads. By executing the search functionality on a separate thread, the SWING event dispatching thread is now free to do other tasks, which the user may invoke. However, the introduction of a thread in the mix raises certain questions.

Let's go ahead and improve our interface as follows.

Initially the Search button, Directory button and the Token field are enabled. When the user clicks on the Search button, these fields are disabled (this prevents the user from clicking the button again). At this point the cancel button is enabled and the status field shows a "Working ..." text. The status field has been introduced to let the user know that the search is still in progress. To create a new thread for search, we need to extend the Thread class. A new class is created called the SearchThread as follows.

The constructor takes in the root directory, the token and the SearchForm itself (so that the output can be displayed on the screen). Note that the FileFinder class is now used from the SearchThread. The thread itself is invoked when the user clicks on the search button as follows

Note that the variable sThread is now a member variable of the class SearchForm. Once the thread is started, the SWING event dispatching thread is free to do other operations. An important operation that the user can do after starting a search is to cancel the search. Note that the search thread calls the method

to show the files on the text area in the middle of the screen.

One of the golden rules of SWING is that, all screen changes (almost) needs to be done from the SWING event dispatching thread. However, here we seem to be doing it from the search thread. Let's see the implementation of the form.setTextArea(..).

The SWING pros will immediately notice that the SwingUtilities.invokeLater solves our problem. The method setTextArea(..) first creates a string from the list of files and then uses invokeLater(..) to set the text area field on the screen. By wrapping the code in SwingUtilities.invokeLater(..) , we effectively call the code from the event dispatcher thread. The invokeLater method simply queues the Runnable object in the event dispatcher queue. The SWING event dispatcher thread detects the message and runs the code in the run() method

Now that we have effectively redrawn the screen, we need a good java implementation to cancel a thread. Java provides us with a particularly important feature to interrupt the thread. The syntax for that is

The interrupted thread needs to periodically check if the thread has been interrupted. If so, the thread needs to break out of any for(..) or while(..) loop and exit from the time consuming task. Note that, this code needs to be put in by the developer and is not automatically available.

Note that java does provide a stop() method on the thread. However, this method has been deprecated and is dangerous to use. The reason for this is that the JVM will simply throw a ThreadDeath error at the line of execution, effectively coming out of the thread. The problem here is that if the data being manipulated is critical (maybe a linked list), there is a high probability that we end up with corrupt data.

In our code, the implementation is simple. The following is done when the user clicks on the cancel button.

Note that the SearchThread class simply delegates all of the responsibility of searching to the FileFinder class. So, somehow the FileFinder class needs know that an interrupt has been sent to it. Any thread can check if it has been interrupted, by using the following

If it returns true, the thread was interrupted. So, the SearchThread would have to periodically check whether it was interrupted and if so exit. Note that there has to be a balance between liveliness of a thread and the performance degradation that may occur due to too many checks for interrupt. Too many checks would improve the liveliness of the thread, since it can respond to the interrupt faster.

In our implementation, the check is done after entering a new directory. Click here to see the complete list of source files for this iteration.

The implementation still has some glaring issues.

1