Changing the SynchroList class
Functionally, there are two changes to the SynchroList
class. The first change is to maintain a set of bits that are associated with the LinkEnumerator
objects that have been created by invoking the elements
method. The second is to update the before
, add
, and remove
methods to note when the Link
object that will be modified by the method is currently part of an enumeration.
The first change is trivial and consists of adding a private variable named players of type ChuckBitSet to the class to note which player IDs are in use. The second is a private method newPlayerID
that is used to return the next available ID. In this design the ID is the index of a bit in the players bit set. The code is shown below.
/* return a new player ID from the list */ private int newPlayerID() { int i = 0; while (players.get(i)) i++; players.set(i); return i; }
As you can see the code above uses a very simple linear search from zero to find the next index. This works well for a small number of threads but should be revisited if more than a few dozen threads are to be accessing a list at the same time. The newPlayerID
method does not need to be synchronized as it is only called from within the LinkEnumerator
constructors which are called in the synchronized elements
methods of SynchroList
.
The changes to the linked list manipulation methods before
, after
, and remove
are slightly more complicated but not terribly so. Consider the code below. This is the original before
method, which inserts a new Link
object into the link list.
private void before(Object o, Link p) { new Link(o, p.prev(), p); }
To update this method, the class needs to ascertain whether or not the Link
object identified by p
is currently part of an enumeration. The updated code is as follows:
private void before(Object o, Link p) { synchronized (p) { while (p.inPlay()) { try { p.wait(); } catch (InterruptedException e) { break; } } new Link(o, p.prev(), p); } }
The method above is now synchronized using the object p
. This ensures that if the thread does block, it will be notified as soon as p
is released. The loop while (p.inplay()) { ... }
guards against multiple threads manipulating the object at once. When a Link
object's release
method is invoked, all threads waiting on it are notified. As the link in question may be participating in several enumerations, however, the waiting thread must hang on until inPlay
returns false before manipulating the object's link references. The after
and remove
methods are changed in exactly the same way, synchronizing on their object first before changing the structure of the linked list on which the object appears. The complete source for this version of the SynchroList
is also available in the Resources section below.
This story, "Using threads with collections, Part 2" was originally published by JavaWorld.