Java Reference
In-Depth Information
ished, as in
Memoizer2
). If not, it creates a
FutureTask
, registers it in the
Map
, and
starts the computation; otherwise it waits for the result of the existing computation. The res-
ult might be available immediately or might be in the process of being computed—but this is
transparent to the caller of
Future.get
.
The
Memoizer3
implementation is almost perfect: it exhibits very good concurrency
(mostly derived from the excellent concurrency of
ConcurrentHashMap
), the result is
returned efficiently if it is already known, and if the computation is in progress by another
thread, newly arriving threads wait patiently for the result. It has only one defect—there is
still a small window of vulnerability in which two threads might compute the same value.
This window is far smaller than in
Memoizer2
, but because the
if
block in
compute
is
still a nonatomic check-thenact sequence, it is possible for two threads to call
compute
with
the same value at roughly the same time, both see that the cache does not contain the desired
value, and both start the computation. This unlucky timing is illustrated in
Figure 5.4
.
Figure 5.4. Unlucky Timing that could Cause
Memoizer3
to Calculate the Same Value Twice.