10.1 Problems Using Threads


10.1 Problems Using Threads

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 applications:

  • Safety. When a method of an object that is also visible in other threads is invoked, it maintains its semantics even when other threads access it (quasi) concurrently, and the object's consistency is not destroyed. To be on the safe side, we need to protect parts of the code against concurrent execution by several threads. Java uses the keyword synchronized for this purpose.

  • Liveness. Each thread is given sufficient opportunity to continue running. Two typical situations jeopardize the liveness of threads:

    • When a deadlock occurs, two or more threads are waiting for one of them to release resources, so that all threads are blocked.

    • Starvation means that one thread no longer gets its turn to continue running. A thread is normally starved due to faulty priority-setting mechanisms.

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 programming 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:

  • A thread starts and ends as expected.

  • Two or more threads are synchronized as expected.

  • The synchronization of threads does not lead to deadlocks.

  • Objects used in several threads are thread-safe.

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 let specific test cases run very frequently.

  • We try to make the thread behavior sufficiently deterministic by targeted timing and additional synchronization.

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:

  • Objects offering asynchronous services. This means that their actual behavior runs in a separate thread, while other threads can trigger and request this service (see Section 10.2).

  • Objects handling a synchronization function, for example, to transport data between threads (see Section 10.3).

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.




Unit Testing in Java. How Tests Drive the Code
Unit Testing in Java: How Tests Drive the Code (The Morgan Kaufmann Series in Software Engineering and Programming)
ISBN: 1558608680
EAN: 2147483647
Year: 2003
Pages: 144
Authors: Johannes Link

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net