Java Reference
In-Depth Information
know its previous value
and
make sure no one else changes or uses that value while you are
in mid-update.
Like most concurrency errors, race conditions don't
always
result in failure: some unlucky
timing is also required. But race conditions can cause serious problems. If
LazyInitRace
is used to instantiate an application-wide registry, having it return different instances from
multiple invocations could cause registrations to be lost or multiple activities to have incon-
sistent views of the set of registered objects. If
UnsafeSequence
is used to generate entity
identifiers in a persistence framework, two distinct objects could end up with the same ID,
violating identity integrity constraints.
2.2.3. Compound Actions
Both
LazyInitRace
and
UnsafeCountingFactorizer
contained a sequence of op-
erations that needed to be
atomic
, or indivisible, relative to other operations on the same state.
To avoid race conditions, there must be a way to prevent other threads from using a variable
while we're in the middle of modifying it, so we can ensure that other threads can observe or
modify the state only before we start or after we finish, but not in the middle.
Operations
A
and
B
are
atomic
with respect to each other if, from the perspective of a thread
executing
A
, when another thread executes
B
, either all of
B
has executed or none of it has.
An
atomic operation
is one that is atomic with respect to all operations, including itself, that
operate on the same state.
If the increment operation in
UnsafeSequence
were atomic, the race condition illustrated
in
Figure 1.1
on page
6
could not occur, and each execution of the increment operation would
have the desired effect of incrementing the counter by exactly one. To ensure thread safety,
check-then-act operations (like lazy initialization) and read-modify-write operations (like in-
crement) must always be atomic. We refer collectively to check-then-act and read-modify-
write sequences as
compound actions
: sequences of operations that must be executed atom-
ically in order to remain thread-safe. In the next section, we'll consider
locking
, Java's builtin
mechanism for ensuring atomicity. For now, we're going to fix the problem another way, by
Listing 2.4. Servlet that Counts Requests Using
AtomicLong
.