- Using Visual J++ -

Chapter 16

Exceptions and Threads


In this chapter, you learn how to write exception-handlers to protect your code from errors. Although you cannot guarantee that you'll think of all exceptions, your exceptions can handle expected problems that often arise when you perform complicated calculations or work with files in your Java applications.

In addition to exceptions, you'll find a brief introduction to threaded programming. The threads of a program are execution paths that make your users think a program can do multiple tasks at the same time. Although threaded programming does not increase your program's speed (actually, threaded programming slows down a program's total execution time somewhat), the user will be more pleased because a threaded program makes the user feel as if the program is more active.

Introduction to Exception Management

Have you heard of the phrase management by exception? The MBE management style is designed for employees who need little supervision to complete their jobs. Generally, the employees who work best under MBE are those who fall into one of the following categories: self-starters, loners, star performers, and quick learners. Instead of wasting time managing these kinds of employees, a manager can spend time managing other parts of the business that need more than routine attention.

The only time MBE employees need management is when an exception occurs from the projected work pattern. That exception might be good or bad. For example, if an employee routinely sells a target of 20,000 widgets every month, and one month goes by where that employee sold only 3,000 widgets, that employee certainly needs management's attention. Management needs to make sure that the shortcoming is reasonable and the employee will be back on track the next month.

Good exceptions should flag management's attention as well as bad exceptions. If an employee's typical sales figures grow from 20,000 widgets to over 1 million units in a month, management must look into the good exception as well. That employee may have wrongly cut prices below wholesale, the accounting department may have made a bookkeeping error in their recording of sales, or the employee may have landed an international contract to supply all of Italy—that employee needs to be praised (such as, being made Supreme Ruler of the company, or something).

As you can see, MBE appears only when things don't go as expected. When things deviate from the normal flow, an exception triggers management's appearance. Management wants to keep things running smoothly and bad as well as good exceptions need to be analyzed for their impact on the future.

The concept of MBE are virtually identical in concept to exceptions that you work with in Visual J++ programs. Now that you've had a rundown of MBE you can better understand what a Visual J++ program looks for when you add exception code to it. Visual J++ can use exceptions to manage a program's execution. A Visual J++ exception occurs when something happens that Visual J++ was not expecting; most usually, a runtime bug.

 
Exceptions do not always appear as bugs, but bugs are the most common reason for Visual J++ exceptions.

Visual J++ Exceptions

Exception handling refers to a Visual J++ program that manages certain exceptions. Just as you cannot always predict every bug that can occur, you cannot always predict the exceptions that you need to handle. Generally, if you find yourself adding extensive error-detecting code to certain actions such as files that your program expects but cannot find, and your error-detection code handles the situation (such as informing the user about the problem), that situation deserves an exception-handling process.

When you set up a Visual J++ with exception handling, you tell the program to throw an exception if the exception situation occurs. In other words, when something goes unexpectedly wrong and you've set up exception code, that error will throw an exception. Other code must catch the exception, meaning that the catching code will take over if and only if the exception (such as an error) takes place. The idea is that the catching code will handle the problem seamlessly without runtime errors entering into the foray and onto the user's screen.

 
Your programs already contain exception handling. If your program attempts to open a file that does not exist, your program will throw that exception to the runtime system and you (or, worse, your end-user) will get a frustrating error message. When you add your own exception handling, you'll attempt to catch the error yourself instead of the error being thrown to the runtime system and resulting in unhandled error messages that your users must deal with.

Whenever you want Visual J++ to throw an exception that you can handle, add the throws keyword to a method's definition. The following statement defines a method without exception-handling code:


public String getAppletInfo()
{ // Body of method goes here

Here is the same method definition with exception-handling code:


public String getAppletInfo() throws exceptionName
{ // Body of method goes here

Not only does this method's definition include the throws keyword, but the method tells the name of the exception that might be thrown: exceptionName. When you call this method from any other location in the program, the throws keyword tells the system that you will handle the exception.

Some Exceptions Are Automatic

Visual J++'s pre-defined class packages include several exception-handling routines that you don't need to worry about. For example, if your application runs out of memory, a memory exception will be thrown without your explicitly looking for the problem and without your having to use throws to warn a method that the method may run out of memory. Table 15.1 lists the pre-defined class exceptions that your code can look for.
 
You should handle certain exceptions. For example, the Java.io class package includes several methods that throw the FileNotFoundException exception. Your end-users will get a runtime error if they run your application and the application attempts to use a file that does not exist.
 
You don't have to specifically throw the common exceptions found in the pre-defined classes. You only need to handle those exceptions (using try and catch as explained in the next section) if you want to respond to those pre-defined exceptions. Your throws clauses need only to describe exceptions that your program generates outside of the common exceptions in the pre-defined classes.

Table 15.1 The Pre-Defined Exceptions

Class package Exceptions
Java.awt AWTException
Java.io EOFException, FileNotFoundException, InterruptedIOException, IOException, UTFDataFormatException
Java.lang ArithmeticException, ArrayIndexOutOfBoundsException, ArrayStoreException, ClassCastException, ClassNotFoundException, CloneNotSupportedException, Exception, IllegalAccessException, IllegalArgumentException, IllegalMonitorException, IllegalThreadStateException, IndexOutOfBoundsException, InstantiationException, InterruptedException, NegativeArraySizeException, NoSuchMethodException, NullPointerException, NumberFormatException, RuntimeException, SecurityException, StringIndexOutOfBoundsException
Java.net MalformedURLException, ProtocolException, SocketException, UnknownHostException, UnknownServiceException
Java.util EmptyStackException, NoSuchElementException

Using try and catch the Exception

What exceptions are thrown? Those you've defined inside your method definition lines with throws and those pre-defined in classes such as Java.lang. When an exception is thrown (when it occurs due to an error or another defined exception trigger), your program will not handle the exception unless you place the exception-causing code inside a try block. A try block is a block of code that begins with the try keyword, such as the following:


try
{ avg = CalculateAvg(); // Computes a divided average
g.drawString("The average is " + avg); // May not execute!
}

In other words, if the average calculation triggers a mathematical error and you've defined the error as an exception using throws or the error is pre-defined, the exception will be handled by your exception handling code. The Java.lang class package defines an exception named ArithmeticException that will be thrown if your running code ever produces an arithmetic error as long as the error occurs in a try block. (The pre-defined nature of ArithmeticException means that you don't have to code it inside the method's definition line with throws.) If, however, another part of this method triggers a mathematical error and that error code does not appear inside a try block, a runtime error will occur. The bottom line is that your exceptions will be runtime errors unless the code that triggers the exception appears in a try block. Therefore, if you are about to write a part of a program that can trigger a runtime error, place that code inside a try block. Instead of an error, if one occurs, your exception-handling code will execute.

The try block has tremendous power that's not always obvious from simple examples. For instance, as long as a try block is still in effect, even if the try block calls other methods that in turn call additional methods, the exception handling will be active. No matter how much work the try block performs and no matter how many levels of method calls the try block executes, the try block's exception handling remains active. Only when the program's execution flies past the try block's closing brace does the exception handler deactivate.

Place the exception-handling code inside a catch block. A catch block handles a particular exception. In other words, you must code a catch block for each exception your program might try to throw. Here is a possible catch block you could write for the ArithmeticException exception:


catch(ArithmeticException excep)
{ g.drawString("An error occurred when you calculated
[ic:ccc]the average.");
g.drawString("Run payroll's pre-process before this
[ic:ccc]application.");
} // The exception will not produce a runtime error now

 
The biggest reason to use exceptions is to let your programs handle problems gracefully and not halt productivity with a messy runtime error message appearing in your user's face. Of course, some exceptions will be so severe that you'll have to halt the program, but you can do so with a more friendly error message such as, "You are missing a file named Actdata.doc. Be sure to create this file before you run this application," instead of a completely cryptic "File not found" error that leaves your users hanging without a clue. Sometimes, you'll be able to handle the error fully; the catch code can create the missing file so the rest of the program can continue.

Of course, some try blocks might throw one of several possible exceptions. Write as many catch blocks as you need to handle all exceptions that may occur. You'll generally place the catch blocks immediately after the try block. If any try block exception appears in the catch blocks, the system will execute your catch block instead of generating an error that would otherwise occur.

The following code shows three possible exception-handling try blocks:


catch(ClassNotFoundException excep)
{ handleClassNotFound(excep); }
catch(NullPointerException excep)
{ handleNullPointer(excep); }
catch(FileNotFoundException excep)
{ handleFileNotFound (excep); }

Visual J++ analyzes the catch blocks sequentially so if you list multiple catch blocks, the first catch block will catch all the exceptions that it can and, failing to do so, the second catch block will attempt to do the same. You can code a catch-all Throwable catch block such a this one:


catch (Throwable excep_
{
// Handle whatever exception is thrown
}

Keep in mind, however, that such a universal catch block will not let any runtime errors appear, even dangerous unexpected errors that you should let go through such as hardware failures.

Exceptions will always handle expected problems and the catch block will not allow the rest of an exception-throwing method to continue. If, however, a method contains both an exception-handling try block as well as code that must execute no matter what happens with the exception, use a finally block. An exception always returns to execute a finally block, indicated with the finally keyword, no matter what the exception-handling code does. Here is a finally block:


finally
{
// This code ALWAYS executes.
// Assume that try and catch blocks
// appeared earlier in this method.
}

Your Own Exceptions

The throw keyword, as opposed to throws that appears in a method definition that defines an exception, lets you trigger an exception that otherwise would not occur. For example, suppose you write a method that receives an array's index value (the array's subscript) and your method is to print the array's value for that index. If the index comes to the method but the index is negative or higher than the array boundary, your method cannot do anything with the index. You can choose to print a message or print a default array element (such as the first element) or you can choose to trigger an exception. The Java.lang class package includes pre-defined exceptions called ArrayIndexOutOfBoundsException and NegativeArraySizeException, and those exceptions can be thrown if you place the throwing code that uses the passed index inside a try block. You can code an if statement at the top of the method and throw the exception yourself like this:


void prArrayElement(int index)
{
if (index <= 0)
{ throw new ArrayIndexOutOfBoundsException(); }
else if (index >= maxIndex)
{ throw new NegativeArraySizeException(); }
// Rest of method appears here and this code will
// execute as long as the exception does not occur

The calling routine catches the exceptions thrown at it and will need to handle the problems, such as adjusting the passed index or gracefully shutting down the program. You would include the exception-handling methods to override those same exception base methods in the Java.lang class package.

If you want to create your own exceptions and throw those exceptions, as opposed to using the exceptions listed in the pre-defined classes, you can do so using the following format that subclasses the pre-built Exception method:


class MyOwnException extends Exception
{
MyOwnException(String excepMessage)
{
handleMyOwn(excepMessage); // handle the exception
}

Threaded Overview

One cannot use Visual J++ for very long without running across the term thread, threaded, or multithreaded code. The Java language is threadable, which simply means you can execute one or more execution paths, (the threads) simultaneously. At least, the code appears to run simultaneously. Unless you have a multi-processor computer, most computers can do only one thing at a time. The simultaneously-executed threads, however, let your users watch a graphic picture paint on the screen while another thread, such as a long sorting routine, takes place at the same time. The two threads actually share processor time but the user can see things happen (the graphics) while waiting on the sorting instead of staring at a black screen while the sort takes place.

Every program is threaded. It's just that many programs contain only a single thread.

Figure 16.1 shows Visual J++'s New Project Workspace wizard's third dialog box which asks if you want a threaded applet. The spinning globe shows animation as long as you keep the Y es, please option checked to indicate that you want a threaded program. If you check the N o, thank you option, the globe stops spinning. The applet wizard knows that your users will not want to see animation in a non-threaded program because too many things have to happen on a Web page for the animation to halt other operations.


FIG. 16.1

You can request a threaded program from the New Project Wizard.

 
Threads can be tricky and they can cause headaches as well as joy. This chapter presents an overview but certainly does not make you an expert in threaded programming. By the time you finish, you will know what's in store when you want to code threads and you will have a good idea of the dangers to watch for.
 
 
If you've run two or more Windows 95 or Windows NT programs at once, and sized the two program windows so that you can view both windows at the same time, you've seen a perfect example of two threads running simultaneously.

One of the most troublesome aspects of threaded programming is synchronizing all the threads. If the threads update or use the same variable, for example, you cannot be sure when the variable will update between the running threads. In other words, you must have a way to synchronize the threads so that they execute in a particular order under certain circumstances.

Understanding the java.lang.Thread Class

The pre-defined java.lang.Thread class contains the bulk of threaded code you'll use if you want to write a threaded program. As with most pre-defined classes, you'll not directly use java.lang.Thread but you'll inherit, or subclass, from java.lang.Thread to add multithreading to your code. java.lang.Thread contains a run() method that knows virtually everything there is about spawning new threads (when a program spawns a new thread, the thread is defined, created, and begins to execute).

Here is the general code you'll write to subclass java.lang.Thread's run() method:


class aThreadClass extends Thread
{
// Class stuff goes here; you
// can define class data and methods
//
public void run() // This does the threaded work
{
// Your code goes here that you want to thread
}
// Rest of class follows
}

The class does nothing until you create an instance of the class. Therefore, you cannot just execute run() until you've instantiated some aThreadClass objects like this:


aThreadClass firstThread = new aThreadClass(); // Creates a
[ic:ccc]new thread
aThreadClass secondThread = new aThreadClass(); // Creates a
[ic:ccc]new thread

Starting and Stopping the Thread

Don't run your thread directly. Instead, use the start() and stop() methods, subclassed automatically from java.lang.Thread, like this:


firstThread.start(); // Executes the first thread's run() method
secondThread.start(); // Executes the second thread's run() method

If you want to stop a threaded process, use stop(). The following statement stops the first thread's execution:

firstThread.stop(); // The first thread halts

The second thread would still continue.

Due to the nature of threading and depending on your program, you could attempt to stop a thread that is not currently running (which triggers the pre-defined exception named IllegalThreadStateException). Therefore, use the isAlive() method to see if a thread is currently running before you stop it like this:


If (firstThread.isAlive()) // Returns true or false
{
firstThread.stop(); // The first thread halts safely
}
// The thread does not need to stop if isAlive() returned false

Temporarily Starting and Stopping the Thread

The start() and stop() methods force a new execution and a complete halt for the thread. If you want to freeze a thread, temporarily, use suspend() and resume(). The following statement suspends but does not stop the first thread:

firstThread.suspend(); // The first thread freezes

When you're ready to resume the thread's execution, use resume() like this:

firstThread.resume(); // The first thread continues

Subclassing Problems

The previous sections were descriptive but naive. You cannot always inherit from Thread. In fact, your applets must directly inherit from Applet so you've got to use an inheritance trick to add threaded programming to your applet. The trick is simple: Use the Runnable keyword.

Consider the following class definition the Visual J++ New Project Wizard writes for you when you design a new threaded class named WebAction:


public class WebAction extends Applet implements Runnable
{

The Runnable keyword tells Visual J++ that the class will be multi-threaded and will contain threaded objects that need java.lang.Thread's run() method (via the start(), stop(), suspend(), and resume() methods described in the previous two sections). You'll now create threaded objects that can run more directly like this:


Thread firstThread = new Thread(); // Creates a new thread
Thread secondThread = new Thread(); // Creates a new thread

Synchronizing Critical Threads

Two or more threads might execute in an order that causes confusion. For example, if a thread updates a variable that another thread prints, you need to determine which thread is critical and synchronize that thread's code so that certain statements execute by themselves without two or more threads running the code at the same time. In other words, two or more threads might be able to execute the same code, as is often the case, but you want to ensure that the code executes by one thread only at any one time.

 
The synchronized keyword ensures that only one thread will execute inside the synchronized method at any one time. Once a thread begins executing a synchronized method, the other threads must wait for the first thread to finish the method before another thread can enter and execute the code.

Here is a method definition that uses synchronized (notice the placement of synchronized appears before the method's return data type unlike Runnable):


synchronized int myCriticalMethod(int a, float b)
{ // Only one thread at a time gets into this method

 
All threads are not suspended when you synchronize one or more parts of your program. The synchronization just ensures that a method will be executed by at most one thread at any one time.

Still have questions about threaded programming? You will because this chapter scratches only the surface. You might want to study the threaded nature of a simple program you create with the New Applet Wizard before diving farther into threads. Threaded programming is a new concept for most programmers, even the programmers who have programmed for years in other languages.

Consider all the advanced threaded topics you can tackle:

Although threaded programming is not an extremely difficult topic, these more advanced capabilities go beyond the scope of this book. Don't miss the forest for the trees; your first priority (stop all other low-priority threads!) should be to learn Visual J++'s environment, practice writing Java-based Visual J++ programs, and discover the more advanced topics, as you're doing now to learn what's ahead for you as a Web site developer and Visual J++ programmer.

Summary

The goal of this chapter is to teach you how to add exception-handling to your Visual J++ programs. When you work with exceptions, you help ensure that your user will see few if any runtime bugs and your programs will more gracefully exit from problems.

In addition to exception handling, Visual J++ supports threaded programming which lets your program go off on two or more execution paths at the same time. Your users will be pleased when you write threaded code because the user will not have to wait for one process to finish before seeing the results of another.

The next chapter continues to describe advanced Visual J++ capabilities by explaining how to implement content and protocol handlers. Although the new Visual J++ programmer will not need content or protocol handlers for a while, you should understand what content and protocol handlers are and you should be aware of the programming requirements they generate.

Here are the points this chapter covered:


Previous Page TOC Next Page

| Previous Chapter | Next Chapter |

| Table of Contents | Book Home Page |

| Que Home Page | Digital Bookshelf | Disclaimer |


To order books from QUE, call us at 800-716-0044 or 317-361-5400.

For comments or technical support for our books and software, select Talk to Us.

© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.