SyncCollections wrap Sync-based control around java.util.Collections.
They are similar in operation to those provided
by java.util.Collection.synchronizedCollection, but have
several extended capabilities.
The Collection interface is conceptually broken into two
parts for purposes of synchronization control. The purely inspective
reader operations are:
- size
- isEmpty
- toArray
- contains
- containsAll
- iterator
The possibly mutative writer operations (which are also
the set of operations that are allowed to throw
UnsupportedOperationException) are:
- add
- addAll
- remove
- clear
- removeAll
- retainAll
SyncCollections can be used with either Syncs or ReadWriteLocks.
When used with
single Syncs, the same lock is used as both the reader and writer lock.
The SyncCollection class cannot itself guarantee that using
a pair of read/write locks will always correctly protect objects, since
Collection implementations are not precluded from internally
performing hidden unprotected state changes within conceptually read-only
operations. However, they do work with current java.util implementations.
(Hopefully, implementations that do not provide this natural
guarantee will be clearly documentented as such.)
This class provides a straight implementation of Collections interface.
In order to conform to this interface, sync failures
due to interruption do NOT result in InterruptedExceptions.
Instead, upon detection of interruption,
- All mutative operations convert the interruption to
an UnsupportedOperationException, while also propagating
the interrupt status of the thread. Thus, unlike normal
java.util.Collections, SyncCollections can transiently
behave as if mutative operations are not supported.
- All read-only operations
attempt to return a result even upon interruption. In some contexts,
such results will be meaningless due to interference, but
provide best-effort status indications that can be useful during
recovery. The cumulative number of synchronization failures encountered
during such operations is accessible using method
synchronizationFailures() .
Non-zero values may indicate serious program errors.
The iterator() method returns a SyncCollectionIterator with
properties and methods that are analogous to those of SyncCollection
itself: hasNext and next are read-only, and remove is mutative.
These methods allow fine-grained controlled access, but do NOT
preclude concurrent modifications from being interleaved with traversals,
which may lead to ConcurrentModificationExceptions.
However, the class also supports method unprotectedIterator
that can be used in conjunction with the readerSync or
writerSync methods to perform locked traversals. For example,
to protect a block of reads:
Sync lock = coll.readerSync();
try {
lock.acquire();
try {
Iterator it = coll.unprotectedIterator();
while (it.hasNext())
System.out.println(it.next());
}
finally {
lock.release();
}
}
catch (InterruptedException ex) { ... }
If you need to protect blocks of writes, you must use some
form of reentrant lock (for example ReentrantLock
or ReentrantWriterPreferenceReadWriteLock ) as the Sync
for the collection in order to allow mutative methods to proceed
while the current thread holds the lock. For example, you might
need to hold a write lock during an initialization sequence:
Collection c = new SyncCollection(new ArrayList(),
new ReentrantWriterPreferenceReadWriteLock());
// ...
c.writeLock().acquire();
try {
for (...) {
Object x = someStream.readObject();
c.add(x); // would block if writeLock not reentrant
}
}
catch (IOException iox) {
...
}
finally {
c.writeLock().release();
}
catch (InterruptedException ex) { ... }
(It would normally be better practice here to not make the
collection accessible until initialization is complete.)
This class does not specifically support use of
timed synchronization through the attempt method. However,
you can obtain this effect via
the TimeoutSync class. For example:
Mutex lock = new Mutex();
TimeoutSync timedLock = new TimeoutSync(lock, 1000); // 1 sec timeouts
Collection c = new SyncCollection(new HashSet(), timedlock);
The same can be done with read-write locks:
ReadWriteLock rwl = new WriterPreferenceReadWriteLock();
Sync rlock = new TimeoutSync(rwl.readLock(), 100);
Sync wlock = new TimeoutSync(rwl.writeLock(), 100);
Collection c = new SyncCollection(new HashSet(), rlock, wlock);
In addition to synchronization control, SyncCollections
may be useful in any context requiring before/after methods
surrounding collections. For example, you can use ObservableSync
to arrange notifications on method calls to collections, as in:
class X {
Collection c;
static class CollectionObserver implements ObservableSync.SyncObserver {
public void onAcquire(Object arg) {
Collection coll = (Collection) arg;
System.out.println("Starting operation on" + coll);
// Other plausible responses include performing integrity
// checks on the collection, updating displays, etc
}
public void onRelease(Object arg) {
Collection coll = (Collection) arg;
System.out.println("Finished operation on" + coll);
}
}
X() {
ObservableSync s = new ObservableSync();
c = new SyncCollection(new HashSet(), s);
s.setNotificationArgument(c);
CollectionObserver obs = new CollectionObserver();
s.attach(obs);
}
...
}
[ Introduction to this package. ]
See Also: LayeredSync See Also: TimeoutSync |