Java Reference
In-Depth Information
/**
* Update the batting statistics for a player for a particular game.
* This method assumes that the player is associated with some batting
* statistics (e.g., that there is a {@link Batter} object associated
* with the player.)
* @param batter Player whose batting statistics are to be updated
* @param results a list of the batting results for a particular game
*/
private void updateBatting(Player batter, List<Batter.AtBatResult> results) {
Batter batStats = batter.asBatter();
synchronized (batter) {
for (Batter.AtBatResult r : results) {
batStats.atBat(r);
}
}
}
Changing the method in this way will ensure that no other thread can access the particular bat-
ter object while the
updateBatting()
method is running. Updates will be done one at a time,
and rosters will need to wait until the update is done to gain access to the
Batter
(which is
part of the
Player
). Doing the same sort of locking for the other updates should ensure that
those calling
getRoster()
will at least see consistent statistics on particular players. It will
still be possible to get a roster that has some players updated for a game that hasn't been re-
corded against other players, but that is something we can live with.
Adding locks to our code can ensure that only a single thread accesses the object that is locked,
helping us to avoid some concurrency bugs. But as things get more complex, locking also
introduces a new class of bugs that revolve around the locks themselves. Suppose, for ex-
ample, that we have implemented a way to update the fielding results for a game. Further,
instead of just passing the batting results for a particular player into
updateBatting()
, we
hand in a structure that contains both the batting and the fielding results. Likewise, for an
updateFielding()
method, we hand in the same structure. Because we are paranoid, at the
end of
updateBatting()
, we call
updateFielding()
(assuming we have a way of checking
whether it has already been called), and when we end the
updateFielding()
method, we call
updateBatting()
(also assuming that we can find out whether it has already been called).
This will work fine, up until the time that the statistics for the same player are simultaneously
being updated by two threads, one of which is starting with the
updateBatting()
method
and the other starting at the
updateFielding()
method. The thread starting with
updateBat-
ting()
will get a lock on the
BattingImpl
object for the player at the same time the
up-