Study guide: Achieve strong performance with threads, Part 1

Brush up on Java terms, learn tips and cautions, review homework assignments, and read Jeff's answers to student questions

Glossary of terms

animation
The act of repeatedly drawing on one surface images that slightly differ from each other to achieve a movement illusion.
concurrent
Simultaneous.
current thread
The thread that associates with the Thread (or Thread subclass) object whose reference Thread.currentThread() returns.
daemon thread
A thread that performs housekeeping (such as garbage collection) and other background tasks that probably do not contribute to the application's main work but are necessary for the application to continue its main work.
multithreading
The act of multiple threads executing byte code instruction sequences in the same program.
runnable
An object created from a class that implements the Runnable interface.
thread
An independent path of execution through program code.
user thread
A thread that performs important work for the program's user that must finish before the application terminates.

Tips and cautions

These tips and cautions will help you write better programs and save you from agonizing over why the compiler produces error messages.

Tips

  • In several places, "Achieve Strong Performance with Threads, Part 1" refers to the concept of a current thread. If you need access to a Thread object that describes the current thread, call Thread's static currentThread() method. Example: Thread current = Thread.currentThread ();.
  • When you face a situation where a class can either extend Thread or implement Runnable, which approach do you choose? If the class already extends another class, you must implement Runnable. However, if that class extends no other class, think about the class name. That name will suggest that the class's objects are either active or passive. For example, the name Ticker suggests that its objects are active—they tick. Thus, the Ticker class would extend Thread, and Ticker objects would be specialized Thread objects. In contrast, Rectangle suggests passive objects—Rectangle objects do nothing on their own. Thus, the Rectangle class would implement Runnable, and Rectangle objects would use Thread objects (for testing or other purposes) instead of being specialized Thread objects.

Cautions

  • Sun Microsystems has deprecated a variety of Thread methods, such as suspend() and resume(), because they can lock up your programs or damage objects. As a result, you should not call them in your code. Consult the SDK documentation for workarounds to those methods.
  • After a thread calls start(), subsequent calls to that method before the run() method exits cause start() to throw a java.lang.IllegalThreadStateException object.
  • Do not attempt to join the current thread to itself because the current thread will wait forever.
  • Do not depend on activeCount()'s return value when iterating over an array. If you do, your program runs the risk of throwing NullPointerException objects. Why? Between the calls to activeCount() and enumerate(Thread [] thdarray), one or more threads might possibly terminate. As a result, enumerate(Thread [] thdarray) would copy fewer thread references into its array. Therefore, think of activeCount()'s return value as a maximum value for array-sizing purposes only. Also, think of enumerate(Thread [] thdarray)'s return value as representing the number of active threads at the time of a program's call to that method.
  • The setDaemon(boolean isDaemon) method throws an IllegalThreadStateException object if a call is made to that method after the thread starts execution.

Reader questions

Find out what questions your fellow readers are asking and my answers to those questions.

Jeff,

In a program I have written, the user selects a file to be parsed by a SAX parser. The interface is started in a thread. It then calls methods in other classes in the following order:

functions.parseXML(file) which calls ioHandler.parseXML(file) which calls xmlHandler.readXML(file) which calls xmlFileReader.parse().

The XMLFilHandler class (third in the list) extends Thread and calls xmlFileReader.parse() in its run() method. My problem is when the parser is parsing, the GUI (graphical user interface) pauses then kicks back in once the parsing is complete. Is this behavior expected with actions that use a lot of processing like parsing?

Toby

Toby, I strongly suspect thread scheduling is at the heart of the behavior you are experiencing. The priority of your XMLFilHandler thread could be greater than the GUI thread's priority, which means the XMLFilHandler thread would preempt the GUI thread more frequently than if both threads had the same priority—assuming your thread scheduler time-slices threads. To see if this is the case, insert a System.out.println (Thread.currentThread ().getPriority ()); method call in one of your GUI's event handlers or paint() methods, and the same method call in your XMLFilHandler class's run() method. Compare both values to see if they are equal or if the priority number in the GUI's event handler/paint() method is less than the run() method's priority number. If so, the GUI thread has a lower priority. To correct the problem, you might try calling Thread.currentThread ().setPriority () with an argument value that exceeds the XMLFilHandler priority. I would experiment with that to see if it improves the GUI thread's performance. Jeff

Homework

Please answer the following questions:

  • What is a third benefit of multithreading?
  • Internally, the join() method calls wait(0) instead of the sleep(long millis) method. Why?

Answers to last month's homework

Last month, I asked you several questions and gave you some exercises to work on. My answers appear in red.

  • What is wrong with the article's SortIntegerArray1 source code?
  • The expression, x [j+1], in Line 19 (if (x [j] > x [j+1])) results in an ArrayIndexOutOfBoundsException because a valid ending index was not specified in the inner for loop. As the code currently reads, the inner for loop tests j against x.length-i, when it should test j against x.length-i-1. The correct code appears below:

    SortIntegerArray1.java

    // SortIntegerArray1.java
    class SortIntegerArray1
    {
       public static void main (String [] args)
       {
          int numbers [] = { 22, 13, 5, 76, 3 };
          sort (numbers);
          for (int i = 0; i < numbers.length; i++)
               System.out.println (numbers [i]);
       }
       static void sort (int [] x)
       {
          for (int i = 0; i < x.length-1; i++)
               for (int j = 0; j < x.length-i-1; j++)
                    if (x [j] > x [j+1])
                    {
                        int temp = x [j];
                        x [j] = x [j+1];
                        x [j+1] = temp;
                    }
       }
    }
    
    
  • Is the following code fragment legal?

    static void a () throws FileNotFoundException
    {
       try
       {
           throw new FileNotFoundException ();
       }
       catch (FileNotFoundException e)
       {
       }
    }
    

    Why or why not? Assume the code fragment is from a program that has an import java.io.FileNotFoundException; directive.

  • The code fragment is legal. Although the method catches the FileNotFoundException, the catch clause can rethrow that object. For that reason, the compiler does not complain that FileNotFoundException is also in the throws clause.

  • Can a try block appear alone in source code?
  • No. Either a catch clause or a finally clause must follow a try block.

  • Why should you use multiple catch clauses instead of one all-encompassing catch clause?
  • You should use multiple catch clauses instead of one all-encompassing catch clause to improve code reliability. In an all-encompassing catch clause, you must use if/if-else statements and expressions involving the instanceof operator to determine what kind of exception object was caught so the program can determine which exception-handling code to execute. You can easily miss something and inadvertently introduce a bug. By using multiple catch clauses, you compartmentalize appropriate exception-handling code in the appropriate catch clauses and lessen the chances of introducing bugs.

  • Does Java allow any class's objects to be thrown?
  • Java allows only Throwable and Throwable subclass objects to be thrown.

  • Rewrite TranslatedExceptionDemo2 to eliminate the ee.initCause (e); method call, but produce the same output.
  • To rewrite TranslatedExceptionDemo2 without the call to initCause(), modify the ExternalException constructor to take a Throwable parameter that identifies the exception's cause. Then, pass that parameter's value to the Exception(String message, Throwable cause) constructor. The complete code follows:

    TranslatedExceptionDemo2.java

    // TranslatedExceptionDemo2.java
    class TranslatedExceptionDemo2
    {
       public static void main (String [] args)
       {
          try
          {
             Library.methodA (); // Line 9
          }
          catch (Library.ExternalException e)
          {
             System.out.println (e.getMessage ());
             // Send stack trace to standard output device, instead of
             // default standard error device
             e.printStackTrace (System.out);
          }
       }
    }
    class Library
    {
       static void methodA () throws ExternalException
       {
          try
          {
             methodB (); // Line 29
          }
          catch (InternalException e)
          {
             System.out.println (e.getMessage ());
             ExternalException ee = new ExternalException (e); // Line 35
             throw ee;
          }
       }
       static class ExternalException extends Exception
       {
          ExternalException (Throwable cause)
          {
             super ("External Exception", cause);
          }
       }
       private static void methodB () throws InternalException
       {
          throw new InternalException (); // Line 50
       }
       private static class InternalException extends Exception
       {
          InternalException ()
          {
             super ("Internal Exception");
          }
       }
    }
    
    
  • Write a program that demonstrates catching an exception object after performing cleanup tasks. Choose appropriate cleanup tasks for that program.
  • See the following source code:

    CatchAfterFinally.java

    // CatchAfterFinally.java
    class CatchAfterFinally
    {
       public static void main (String [] args)
       {
          try
          {
              someMethod1 ();
          }
          catch (MyException e)
          {
             System.out.println ("Catching exception: " + e.getMessage ());
          }
       }
       static void someMethod1 () throws MyException
       {
          try
          {
              someMethod2 ();
          }
          finally
          {
              System.out.println ("Cleaning up");
          }
       }
       static void someMethod2 () throws MyException
       {
          throw new MyException ("Some exception");
       }
    }
    class MyException extends Exception
    {
       MyException (String msg)
       {
          super (msg);
       }
    }
    

    someMethod2() throws a MyException object. Before the JVM exits the someMethod1() method, in its search for an appropriate catch clause, the JVM executes the finally clause. When run, you see the following output:

    Cleaning up
    Catching exception: Some exception