Code - Class EDU.oswego.cs.dl.util.concurrent.SyncCollection


1 /*
2   File: SyncCollection.java
3
4   Originally written by Doug Lea and released into the public domain.
5   This may be used for any purposes whatsoever without acknowledgment.
6   Thanks for the assistance and support of Sun Microsystems Labs,
7   and everyone contributing, testing, and using this code.
8
9   History:
10   Date Who What
11    1Aug1998 dl Create public version
12 */

13
14 package EDU.oswego.cs.dl.util.concurrent;
15 import java.util.*;
16
17 /**
18  * SyncCollections wrap Sync-based control around java.util.Collections.
19  * They are similar in operation to those provided
20  * by java.util.Collection.synchronizedCollection, but have
21  * several extended capabilities.
22  * <p>
23  * The Collection interface is conceptually broken into two
24  * parts for purposes of synchronization control. The purely inspective
25  * reader operations are:
26  * <ul>
27  * <li> size
28  * <li> isEmpty
29  * <li> toArray
30  * <li> contains
31  * <li> containsAll
32  * <li> iterator
33  * </ul>
34  * The possibly mutative writer operations (which are also
35  * the set of operations that are allowed to throw
36  * UnsupportedOperationException) are:
37  * <ul>
38  * <li> add
39  * <li> addAll
40  * <li> remove
41  * <li> clear
42  * <li> removeAll
43  * <li> retainAll
44  * </ul>
45  *
46  * <p>
47  * SyncCollections can be used with either Syncs or ReadWriteLocks.
48  * When used with
49  * single Syncs, the same lock is used as both the reader and writer lock.
50  * The SyncCollection class cannot itself guarantee that using
51  * a pair of read/write locks will always correctly protect objects, since
52  * Collection implementations are not precluded from internally
53  * performing hidden unprotected state changes within conceptually read-only
54  * operations. However, they do work with current java.util implementations.
55  * (Hopefully, implementations that do not provide this natural
56  * guarantee will be clearly documentented as such.)
57  * <p>
58  * This class provides a straight implementation of Collections interface.
59  * In order to conform to this interface, sync failures
60  * due to interruption do NOT result in InterruptedExceptions.
61  * Instead, upon detection of interruption,
62  * <ul>
63  * <li> All mutative operations convert the interruption to
64  * an UnsupportedOperationException, while also propagating
65  * the interrupt status of the thread. Thus, unlike normal
66  * java.util.Collections, SyncCollections can <em>transiently</em>
67  * behave as if mutative operations are not supported.
68  * <li> All read-only operations
69  * attempt to return a result even upon interruption. In some contexts,
70  * such results will be meaningless due to interference, but
71  * provide best-effort status indications that can be useful during
72  * recovery. The cumulative number of synchronization failures encountered
73  * during such operations is accessible using method
74  * <code>synchronizationFailures()</code>.
75  * Non-zero values may indicate serious program errors.
76  * </ul>
77  * <p>
78  * The iterator() method returns a SyncCollectionIterator with
79  * properties and methods that are analogous to those of SyncCollection
80  * itself: hasNext and next are read-only, and remove is mutative.
81  * These methods allow fine-grained controlled access, but do <em>NOT</em>
82  * preclude concurrent modifications from being interleaved with traversals,
83  * which may lead to ConcurrentModificationExceptions.
84  * However, the class also supports method <code>unprotectedIterator</code>
85  * that can be used in conjunction with the <code>readerSync</code> or
86  * <code>writerSync</code> methods to perform locked traversals. For example,
87  * to protect a block of reads:
88  * <pre>
89  * Sync lock = coll.readerSync();
90  * try {
91  * lock.acquire();
92  * try {
93  * Iterator it = coll.unprotectedIterator();
94  * while (it.hasNext())
95  * System.out.println(it.next());
96  * }
97  * finally {
98  * lock.release();
99  * }
100  * }
101  * catch (InterruptedException ex) { ... }
102  * </pre>
103  * If you need to protect blocks of writes, you must use some
104  * form of <em>reentrant</em> lock (for example <code>ReentrantLock</code>
105  * or <code>ReentrantWriterPreferenceReadWriteLock</code>) as the Sync
106  * for the collection in order to allow mutative methods to proceed
107  * while the current thread holds the lock. For example, you might
108  * need to hold a write lock during an initialization sequence:
109  * <pre>
110  * Collection c = new SyncCollection(new ArrayList(),
111  * new ReentrantWriterPreferenceReadWriteLock());
112  * // ...
113  * c.writeLock().acquire();
114  * try {
115  * for (...) {
116  * Object x = someStream.readObject();
117  * c.add(x); // would block if writeLock not reentrant
118  * }
119  * }
120  * catch (IOException iox) {
121  * ...
122  * }
123  * finally {
124  * c.writeLock().release();
125  * }
126  * catch (InterruptedException ex) { ... }
127  * </pre>
128  * <p>
129  * (It would normally be better practice here to not make the
130  * collection accessible until initialization is complete.)
131  * <p>
132  * This class does not specifically support use of
133  * timed synchronization through the attempt method. However,
134  * you can obtain this effect via
135  * the TimeoutSync class. For example:
136  * <pre>
137  * Mutex lock = new Mutex();
138  * TimeoutSync timedLock = new TimeoutSync(lock, 1000); // 1 sec timeouts
139  * Collection c = new SyncCollection(new HashSet(), timedlock);
140  * </pre>
141  * <p>
142  * The same can be done with read-write locks:
143  * <pre>
144  * ReadWriteLock rwl = new WriterPreferenceReadWriteLock();
145  * Sync rlock = new TimeoutSync(rwl.readLock(), 100);
146  * Sync wlock = new TimeoutSync(rwl.writeLock(), 100);
147  * Collection c = new SyncCollection(new HashSet(), rlock, wlock);
148  * </pre>
149  * <p>
150  * In addition to synchronization control, SyncCollections
151  * may be useful in any context requiring before/after methods
152  * surrounding collections. For example, you can use ObservableSync
153  * to arrange notifications on method calls to collections, as in:
154  * <pre>
155  * class X {
156  * Collection c;
157  *
158  * static class CollectionObserver implements ObservableSync.SyncObserver {
159  * public void onAcquire(Object arg) {
160  * Collection coll = (Collection) arg;
161  * System.out.println("Starting operation on" + coll);
162  * // Other plausible responses include performing integrity
163  * // checks on the collection, updating displays, etc
164  * }
165  * public void onRelease(Object arg) {
166  * Collection coll = (Collection) arg;
167  * System.out.println("Finished operation on" + coll);
168  * }
169  * }
170  *
171  * X() {
172  * ObservableSync s = new ObservableSync();
173  * c = new SyncCollection(new HashSet(), s);
174  * s.setNotificationArgument(c);
175  * CollectionObserver obs = new CollectionObserver();
176  * s.attach(obs);
177  * }
178  * ...
179  * }
180  * </pre>
181  *
182  * <p>[<a HREF="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
183  * @see LayeredSync
184  * @see TimeoutSync
185 **/

186
187
188 public class SyncCollection implements Collection {
189   protected final Collection c_; // Backing Collection
190
protected final Sync rd_; // sync for read-only methods
191
protected final Sync wr_; // sync for mutative methods
192

193   protected final SynchronizedLong syncFailures_ = new SynchronizedLong(0);
194
195   /**
196    * Create a new SyncCollection protecting the given collection,
197    * and using the given sync to control both reader and writer methods.
198    * Common, reasonable choices for the sync argument include
199    * Mutex, ReentrantLock, and Semaphores initialized to 1.
200    * <p>
201    * <b>Sample Usage</b>
202    * <pre>
203    * Collection c = new SyncCollection(new ArrayList(), new Mutex());
204    * </pre>
205    **/

206   public SyncCollection(Collection collection, Sync sync) {
207     this (collection, sync, sync);
208   }
209
210
211   /**
212    * Create a new SyncCollection protecting the given collection,
213    * and using the given ReadWriteLock to control reader and writer methods.
214    * <p>
215    * <b>Sample Usage</b>
216    * <pre>
217    * Collection c = new SyncCollection(new HashSet(),
218    * new WriterPreferenceReadWriteLock());
219    * </pre>
220    **/

221   public SyncCollection(Collection collection, ReadWriteLock rwl) {
222     this (collection, rwl.readLock(), rwl.writeLock());
223   }
224
225   /**
226    * Create a new SyncCollection protecting the given collection,
227    * and using the given pair of locks to control reader and writer methods.
228    **/

229   public SyncCollection(Collection collection, Sync readLock, Sync writeLock) {
230     c_ = collection;
231     rd_ = readLock;
232     wr_ = writeLock;
233   }
234
235   /**
236    * Return the Sync object managing read-only operations
237    **/

238       
239   public Sync readerSync() {
240     return rd_;
241   }
242
243   /**
244    * Return the Sync object managing mutative operations
245    **/

246
247   public Sync writerSync() {
248     return wr_;
249   }
250
251   /**
252    * Return the number of synchronization failures for read-only operations
253    **/

254   public long syncFailures() {
255     return syncFailures_.get();
256   }
257
258
259   /** Try to acquire sync before a reader operation; record failure **/
260   protected boolean beforeRead() {
261     try {
262       rd_.acquire();
263       return false;
264     }
265     catch (InterruptedException ex) {
266       syncFailures_.increment();
267       return true;
268     }
269   }
270
271   /** Clean up after a reader operation **/
272   protected void afterRead(boolean wasInterrupted) {
273     if (wasInterrupted) {
274       Thread.currentThread().interrupt();
275     }
276     else
277       rd_.release();
278   }
279
280
281
282   public int size() {
283     boolean wasInterrupted = beforeRead();
284     try {
285       return c_.size();
286     }
287     finally {
288       afterRead(wasInterrupted);
289     }
290   }
291
292   public boolean isEmpty() {
293     boolean wasInterrupted = beforeRead();
294     try {
295       return c_.isEmpty();
296     }
297     finally {
298       afterRead(wasInterrupted);
299     }
300   }
301
302   public boolean contains(Object o) {
303     boolean wasInterrupted = beforeRead();
304     try {
305       return c_.contains(o);
306     }
307     finally {
308       afterRead(wasInterrupted);
309     }
310   }
311
312   public Object[] toArray() {
313     boolean wasInterrupted = beforeRead();
314     try {
315       return c_.toArray();
316     }
317     finally {
318       afterRead(wasInterrupted);
319     }
320   }
321
322   public Object[] toArray(Object[] a) {
323     boolean wasInterrupted = beforeRead();
324     try {
325       return c_.toArray(a);
326     }
327     finally {
328       afterRead(wasInterrupted);
329     }
330   }
331
332   public boolean containsAll(Collection coll) {
333     boolean wasInterrupted = beforeRead();
334     try {
335       return c_.containsAll(coll);
336     }
337     finally {
338       afterRead(wasInterrupted);
339     }
340   }
341
342
343   public boolean add(Object o) {
344     try {
345       wr_.acquire();
346       try {
347         return c_.add(o);
348       }
349       finally {
350         wr_.release();
351       }
352     }
353     catch (InterruptedException ex) {
354       Thread.currentThread().interrupt();
355       throw new UnsupportedOperationException();
356     }
357   }
358
359   public boolean remove(Object o) {
360     try {
361       wr_.acquire();
362       try {
363         return c_.remove(o);
364       }
365       finally {
366         wr_.release();
367       }
368     }
369     catch (InterruptedException ex) {
370       Thread.currentThread().interrupt();
371       throw new UnsupportedOperationException();
372     }
373   }
374
375   public boolean addAll(Collection coll) {
376     try {
377       wr_.acquire();
378       try {
379         return c_.addAll(coll);
380       }
381       finally {
382         wr_.release();
383       }
384     }
385     catch (InterruptedException ex) {
386       Thread.currentThread().interrupt();
387       throw new UnsupportedOperationException();
388     }
389   }
390
391   public boolean removeAll(Collection coll) {
392     try {
393       wr_.acquire();
394       try {
395         return c_.removeAll(coll);
396       }
397       finally {
398         wr_.release();
399       }
400     }
401     catch (InterruptedException ex) {
402       Thread.currentThread().interrupt();
403       throw new UnsupportedOperationException();
404     }
405   }
406
407
408   public boolean retainAll(Collection coll) {
409     try {
410       wr_.acquire();
411       try {
412         return c_.retainAll(coll);
413       }
414       finally {
415         wr_.release();
416       }
417     }
418     catch (InterruptedException ex) {
419       Thread.currentThread().interrupt();
420       throw new UnsupportedOperationException();
421     }
422   }
423
424     
425   public void clear() {
426     try {
427       wr_.acquire();
428       try {
429         c_.clear();
430       }
431       finally {
432         wr_.release();
433       }
434     }
435     catch (InterruptedException ex) {
436       Thread.currentThread().interrupt();
437       throw new UnsupportedOperationException();
438     }
439   }
440
441
442   /** Return the base iterator of the underlying collection **/
443   public Iterator unprotectedIterator() {
444     boolean wasInterrupted = beforeRead();
445     try {
446       return c_.iterator();
447     }
448     finally {
449       afterRead(wasInterrupted);
450     }
451   }
452
453   public Iterator iterator() {
454     boolean wasInterrupted = beforeRead();
455     try {
456       return new SyncCollectionIterator(c_.iterator());
457     }
458     finally {
459       afterRead(wasInterrupted);
460     }
461   }
462
463   public class SyncCollectionIterator implements Iterator {
464     protected final Iterator baseIterator_;
465
466     SyncCollectionIterator(Iterator baseIterator) {
467       baseIterator_ = baseIterator;
468     }
469
470     public boolean hasNext() {
471       boolean wasInterrupted = beforeRead();
472       try {
473         return baseIterator_.hasNext();
474       }
475       finally {
476         afterRead(wasInterrupted);
477       }
478     }
479
480     public Object next() {
481       boolean wasInterrupted = beforeRead();
482       try {
483         return baseIterator_.next();
484       }
485       finally {
486         afterRead(wasInterrupted);
487       }
488     }
489
490     public void remove() {
491       try {
492         wr_.acquire();
493         try {
494            baseIterator_.remove();
495         }
496         finally {
497           wr_.release();
498         }
499       }
500       catch (InterruptedException ex) {
501         Thread.currentThread().interrupt();
502         throw new UnsupportedOperationException();
503       }
504     }
505
506   }
507 }
508
509
510

Java API By Example, From Geeks To Geeks. | Conditions of Use | About Us © 2002 - 2005, KickJava.com, or its affiliates