Java Reference
In-Depth Information
sources are not always requested in the same order, thread A could be holding a connection
to database D 1 while waiting for a connection to database D 2 , and thread B could be holding
a connection to D 2 while waiting for a connection to D 1 . (The larger the pools are, the less
likely this is to occur; if each pool has N connections, deadlock requires N sets of cyclically
waiting threads and a lot of unlucky timing.)
Another form of resource-based deadlock is thread-starvation deadlock . We saw an example
of this hazard in Section 8.1.1 , where a task that submits a task and waits for its result ex-
ecutes in a single-threaded Executor . In that case, the first task will wait forever, perman-
ently stalling that task and all others waiting to execute in that Executor . Tasks that wait
for the results of other tasks are the primary source of thread-starvation deadlock; bounded
pools and interdependent tasks do not mix well.
10.2. Avoiding and Diagnosing Deadlocks
A program that never acquires more than one lock at a time cannot experience lock-ordering
deadlock. Of course, this is not always practical, but if you can get away with it, it's a lot less
work. If you must acquire multiple locks, lock ordering must be a part of your design: try to
minimize the number of potential locking interactions, and follow and document a lock-or-
dering protocol for locks that may be acquired together.
In programs that use fine-grained locking, audit your code for deadlock freedom using a two-
part strategy: first, identify where multiple locks could be acquired (try to make this a small
set), and then perform a global analysis of all such instances to ensure that lock ordering
is consistent across your entire program. Using open calls wherever possible simplifies this
analysis substantially. With no non-open calls, finding instances where multiple locks are ac-
quired is fairly easy, either by code review or by automated bytecode or source code analysis.
10.2.1. Timed Lock Attempts
Another technique for detecting and recovering from deadlocks is to use the timed tryLock
feature of the explicit Lock classes (see Chapter 13 ) instead of intrinsic locking. Where in-
trinsic locks wait forever if they cannot acquire the lock, explicit locks let you specify a
timeout after which tryLock returns failure. By using a timeout that is much longer than
you expect acquiring the lock to take, you can regain control when something unexpected
happens. ( Listing 13.3 on page 280 shows an alternative implementation of transfer-
Money using the polled tryLock with retries for probabilistic deadlock avoidance.)
Search WWH ::




Custom Search