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
(orThread
subclass) object whose referenceThread.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, callThread
‘s staticcurrentThread()
method. Example:Thread current = Thread.currentThread ();
. - When you face a situation where a class can either extend
Thread
or implementRunnable
, which approach do you choose? If the class already extends another class, you must implementRunnable
. 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 nameTicker
suggests that its objects are active—they tick. Thus, theTicker
class would extendThread
, andTicker
objects would be specializedThread
objects. In contrast,Rectangle
suggests passive objects—Rectangle
objects do nothing on their own. Thus, theRectangle
class would implementRunnable
, andRectangle
objects would useThread
objects (for testing or other purposes) instead of being specializedThread
objects.
Cautions
- Sun Microsystems has deprecated a variety of
Thread
methods, such assuspend()
andresume()
, 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 therun()
method exits causestart()
to throw ajava.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 throwingNullPointerException
objects. Why? Between the calls toactiveCount()
andenumerate(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 ofactiveCount()
‘s return value as a maximum value for array-sizing purposes only. Also, think ofenumerate(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 anIllegalThreadStateException
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 callswait(0)
instead of thesleep(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 anArrayIndexOutOfBoundsException
because a valid ending index was not specified in the innerfor
loop. As the code currently reads, the innerfor
loop testsj
againstx.length-i
, when it should testj
againstx.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
, thecatch
clause can rethrow that object. For that reason, the compiler does not complain thatFileNotFoundException
is also in thethrows
clause. - Can a
try
block appear alone in source code? -
No. Either a
catch
clause or afinally
clause must follow atry
block. - Why should you use multiple
catch
clauses instead of one all-encompassingcatch
clause? -
You should use multiple
catch
clauses instead of one all-encompassingcatch
clause to improve code reliability. In an all-encompassingcatch
clause, you must use if/if-else statements and expressions involving theinstanceof
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 multiplecatch
clauses, you compartmentalize appropriate exception-handling code in the appropriatecatch
clauses and lessen the chances of introducing bugs. - Does Java allow any class’s objects to be thrown?
-
Java allows only
Throwable
andThrowable
subclass objects to be thrown. - Rewrite
TranslatedExceptionDemo2
to eliminate theee.initCause (e);
method call, but produce the same output. -
To rewrite
TranslatedExceptionDemo2
without the call toinitCause()
, modify theExternalException
constructor to take aThrowable
parameter that identifies the exception’s cause. Then, pass that parameter’s value to theException(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 aMyException
object. Before the JVM exits thesomeMethod1()
method, in its search for an appropriatecatch
clause, the JVM executes thefinally
clause. When run, you see the following output:Cleaning up Catching exception: Some exception