Java Reference
In-Depth Information
2.2.2. Example: Race Conditions in Lazy Initialization
A common idiom that uses check-then-act is
lazyinitialization
. The goal of lazy initialization
is to defer initializing an object until it is actually needed while at the same time ensuring
tion idiom. The
getInstance
method first checks whether the
ExpensiveObject
has
already been initialized, in which case it returns the existing instance; otherwise it creates a
new instance and returns it after retaining a reference to it so that future invocations can avoid
the more expensive code path.
Listing 2.3. Race Condition in Lazy Initialization.
Don't do this.
LazyInitRace
has race conditions that can undermine its correctness. Say that threads
A
and
B
execute
getInstance
at the same time.
A
sees that
instance
is
null
, and in-
stantiates a new
ExpensiveObject
.
B
also checks if
instance
is
null
. Whether
in-
stance
is
null
at this point depends unpredictably on timing, including the vagaries of
scheduling and how long
A
takes to instantiate the
ExpensiveObject
and set the
in-
stance
field. If
instance
is
null
when
B
examines it, the two callers to
getIn-
stance
may receive two different results, even though
getInstance
is always supposed
to return the same instance.
The hit-counting operation in
UnsafeCountingFactorizer
has another sort of race
condition. Read-modify-write operations, like incrementing a counter, define a transforma-
tion of an object's state in terms of its previous state. To increment a counter, you have to