Java Reference
In-Depth Information
and then the first two threads run concurrently for some amount of time, and only eventually
are all the threads running concurrently. (The same thing happens at the end of the run: the
threads that got a head start also finish early.)
We presented a technique for mitigating this problem in Section 5.5.1 , using a Coun-
tDownLatch as a starting gate and another as a finish gate. Another way to get the same
effect is to use a CyclicBarrier , initialized with the number of worker threads plus
one, and have the worker threads and the test driver wait at the barrier at the beginning and
end of their run. This ensures that all threads are up and running before any start working.
PutTakeTest uses this technique to coordinate starting and stopping the worker threads,
creating more potential concurrent interleavings. We still can't guarantee that the scheduler
won't run each thread to completion sequentially, but making the runs long enough reduces
the extent to which scheduling distorts our results.
The final trick employed by PutTakeTest is to use a deterministic termination criterion so
that no additional inter-thread coordination is needed to figure out when the test is finished.
The test method starts exactly as many producers as consumers and each of them put s or
take s the same number of elements, so the total number of items added and removed is the
same.
Tests like PutTakeTest tend to be good at finding safety violations. For example, a com-
mon error in implementing semaphore-controlled buffers is to forget that the code actu-
ally doing the insertion and extraction requires mutual exclusion (using synchronized or
ReentrantLock ). A sample run of PutTakeTest with a version of BoundedBuffer
that omits making doInsert and doExtract synchronized fails fairly quickly. Run-
ning PutTakeTest with a few dozen threads iterating a few million times on buffers of
various capacity on various systems increases our confidence about the lack of data corrup-
tion in put and take .
Tests should be run on multiprocessor systems to increase the diversity of potential interleav-
ings. However, having more than a few CPUs does not necessarily make tests more effective.
To maximize the chance of detecting timing-sensitive data races, there should be more active
threads than CPUs, so that at any given time some threads are running and some are switched
out, thus reducing the predicatability of interactions between threads.
Search WWH ::




Custom Search