Java Reference
In-Depth Information
long time. Also, if the collection is locked as in Listing 5.4 , doSomething is being called
with a lock held, which is a risk factor for deadlock (see Chapter 10 ) . Even in the absence of
starvation or deadlock risk, locking collections for significant periods of time hurts applica-
tion scalability. The longer a lock is held, the more likely it is to be contended, and if many
threads are blocked waiting for a lock throughput and CPU utilization can suffer (see Chapter
11 ) .
An alternative to locking the collection during iteration is to clone the collection and iterate
the copy instead. Since the clone is thread-confined, no other thread can modify it during
iteration, eliminating the possibility of ConcurrentModificationException . (The
collection still must be locked during the clone operation itself.) Cloning the collection has
an obvious performance cost; whether this is a favorable tradeoff depends on many factors
including the size of the collection, how much work is done for each element, the relative fre-
quency of iteration compared to other collection operations, and responsiveness and through-
put requirements.
5.1.3. Hidden Iterators
While locking can prevent iterators from throwing ConcurrentModificationExcep-
tion , you have to remember to use locking everywhere a shared collection might be iterated.
This is trickier than it sounds, as iterators are sometimes hidden, as in HiddenIterator
in Listing 5.6 . There is no explicit iteration in HiddenIterator , but the code in bold en-
tails iteration just the same. The string concatenation gets turned by the compiler into a call to
StringBuilder . append(Object) , which in turn invokes the collection's toString
method—and the implementation of toString in the standard collections iterates the col-
lection and calls toString on each element to produce a nicely formatted representation of
the collection's contents.
The addTenThings method could throw ConcurrentModificationException ,
because the collection is being iterated by toString in the process of preparing the debug-
ging message. Of course, the real problem is that HiddenIterator is not thread-safe; the
HiddenIterator lock should be acquired before using set in the println call, but de-
bugging and logging code commonly neglect to do this.
The real lesson here is that the greater the distance between the state and the synchronization
that guards it, the more likely that someone will forget to use proper synchronization when
accessing that state. If HiddenIterator wrapped the HashSet with a synchron-
izedSet , encapsulating the synchronization, this sort of error would not occur.
Search WWH ::




Custom Search