img
. .
Another detail to note in this code is the joining code. We first lock the lock, get the next thread
object, then unlock it again before calling join(). Why? Well try it! Just move the join()
inside the synchronized section. Deadlock again! (The main thread is holding the lock, blocking
on a join, while the successful searcher needs to lock the lock before it can cancel the other
searchers.)
This is actually a very interesting bit of code. As soon as the main thread has created the last
searcher thread and released the lock, the array can be treated as a constant--no other changes will
be made to it until all searcher threads exit. This means that the main thread, which knows that the
array is a constant, could dispense with locking the array in the join code. The searcher threads,
which don't know when the array becomes a constant, must synchronize on that state somehow.
(What if one of the searchers found the PID before the main thread had finished creating the rest?
It might execute the cancellation loop and miss the not-yet-created threads.)
Cleanup
When a thread is cancelled, POSIX provides a way to clean up the thread's state through a set of
cleanup handlers that are called upon the exiting of a thread. These are functions of one argument,
which you define and then push onto a thread's cleanup stack. Should the thread exit [either via
cancellation or a call to pthread_exit()], the functions on the stack will be run on the
argument supplied. Should the thread not be cancelled, you may pop the functions off when they
are no longer required (Code Example 9-16).
Example 9-16 How POSIX Cleanup Handlers Are Used
pointer = malloc(100);
pthread_cleanup_push(free, pointer);
use(pointer);
free(pointer);
pthread_cleanup_pop(0);
The general idea for cancellation is that programmers will write their programs such that sections
of code that allocate resources, obtain locks, etc., are preceded (or followed) immediately by
cleanup handler pushes. The cleanup handlers will be responsible for freeing resources,
reestablishing data invariants, and freeing locks.
Java provides a neater solution to this problem. First, because Java has a garbage collector, there is
no need to free memory, as in C/C++. Second, in those instances where you do need to release
specific resources (close file descriptors, remove entries from lists, etc.), you can use the existing
Java constructs of exception handlers and finally sections to ensure that the exit code is
executed (Code Example 9-17).
Example 9-17 How InterruptedException Handlers Clean Up
try {
open_file();
try {
do_stuff();
} catch (InterruptedException ie) {
clean_up_stuff();
}
} finally {
close_file();
}
Search WWH :
Custom Search
Previous Page
Multithreaded Programming with JAVA - Topic Index
Next Page
Multithreaded Programming with JAVA - Bookmarks
Home