As long as two threads simply run alongside each other, they won't cause us any trouble, at least no more so than usual sequential programs. Problems normally arise when several threads try to synchronize (e.g., to exchange information) or when they have to synchronize because several of them want to access the same data (i.e., the same objects). Many developers are not aware of (potentially) concurrent access by several threads. For example, all event processing of AWT occurs in a separate thread. And servlet instances also have to expect that several threads may invoke their service() method concurrently. Similarly, the danger in singletons is often overlooked when they are planted into a multi-thread environment, when they were originally not conceived for parallel access. The following goals should be kept in mind when designing multi-threaded apps:

Unfortunately, there are no universal rules on how to optimally achieve these goals for all concurrent programs. Doug Lea's [00] seminal work on concurrent coding in Java discusses many traps and patterns to avoid them. Naturally, we would like to be able to minimize the probability of these problems from occurring in the first place by using appropriate unit tests. For this reason, apart from the "normal" functionality, the following things should also be tested in (multi-)threaded programming:

Nondeterminism

Testing programs with several threads is more difficult due to the fact that it is usually impossible in practice to run exactly the same program cycle a second time. The operating system's scheduler and the specific thread implementation of the JVM used determine when processor time is allocated to a specific thread. For this reason, certain error situations occur only under very specific or extremely rare circumstances. This means that we can never be really sure whether or not a faulty behavior was removed only because it has not occurred in the last few test runs. When testing, we try to control this nondeterministic behavior by two methods:

We will use both techniques later.

Target Objects

As usual in unit tests, we always concentrate on small units. However, the ways in which threads can handle objects and how objects attempt to be thread-safe are inexhaustible. Among this enormous complexity, there are two types of objects that occur frequently, such that we want to concentrate our testing efforts on these objects:

Of course, there are mixed types between these two object types; they offer asynchronous services and synchronize the triggering of these services in one way or another.