001: /*
002: File: ReentrantWriterPreferenceReadWriteLock.java
003:
004: Originally written by Doug Lea and released into the public domain.
005: This may be used for any purposes whatsoever without acknowledgment.
006: Thanks for the assistance and support of Sun Microsystems Labs,
007: and everyone contributing, testing, and using this code.
008:
009: History:
010: Date Who What
011: 26aug1998 dl Create public version
012: 7sep2000 dl Readers are now also reentrant
013: 19jan2001 dl Allow read->write upgrades if the only reader
014: 10dec2002 dl Throw IllegalStateException on extra release
015: */
016:
017: package EDU.oswego.cs.dl.util.concurrent;
018:
019: import java.util.*;
020:
021: /**
022: * A writer-preference ReadWriteLock that allows both readers and
023: * writers to reacquire
024: * read or write locks in the style of a ReentrantLock.
025: * Readers are not allowed until all write locks held by
026: * the writing thread have been released.
027: * Among other applications, reentrancy can be useful when
028: * write locks are held during calls or callbacks to methods that perform
029: * reads under read locks.
030: * <p>
031: * <b>Sample usage</b>. Here is a code sketch showing how to exploit
032: * reentrancy to perform lock downgrading after updating a cache:
033: * <pre>
034: * class CachedData {
035: * Object data;
036: * volatile boolean cacheValid;
037: * ReentrantWriterPreferenceReadWriteLock rwl = ...
038: *
039: * void processCachedData() {
040: * rwl.readLock().acquire();
041: * if (!cacheValid) {
042: *
043: * // upgrade lock:
044: * rwl.readLock().release(); // must release first to obtain writelock
045: * rwl.writeLock().acquire();
046: * if (!cacheValid) { // recheck
047: * data = ...
048: * cacheValid = true;
049: * }
050: * // downgrade lock
051: * rwl.readLock().acquire(); // reacquire read without giving up lock
052: * rwl.writeLock().release(); // release write, still hold read
053: * }
054: *
055: * use(data);
056: * rwl.readLock().release();
057: * }
058: * }
059: * </pre>
060: *
061: *
062: * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
063: * @see ReentrantLock
064: **/
065:
066: public class ReentrantWriterPreferenceReadWriteLock extends
067: WriterPreferenceReadWriteLock {
068:
069: /** Number of acquires on write lock by activeWriter_ thread **/
070: protected long writeHolds_ = 0;
071:
072: /** Number of acquires on read lock by any reader thread **/
073: protected HashMap readers_ = new HashMap();
074:
075: /** cache/reuse the special Integer value one to speed up readlocks **/
076: protected static final Integer IONE = new Integer(1);
077:
078: protected boolean allowReader() {
079: return (activeWriter_ == null && waitingWriters_ == 0)
080: || activeWriter_ == Thread.currentThread();
081: }
082:
083: protected synchronized boolean startRead() {
084: Thread t = Thread.currentThread();
085: Object c = readers_.get(t);
086: if (c != null) { // already held -- just increment hold count
087: readers_
088: .put(t, new Integer(((Integer) (c)).intValue() + 1));
089: ++activeReaders_;
090: return true;
091: } else if (allowReader()) {
092: readers_.put(t, IONE);
093: ++activeReaders_;
094: return true;
095: } else
096: return false;
097: }
098:
099: protected synchronized boolean startWrite() {
100: if (activeWriter_ == Thread.currentThread()) { // already held; re-acquire
101: ++writeHolds_;
102: return true;
103: } else if (writeHolds_ == 0) {
104: if (activeReaders_ == 0
105: || (readers_.size() == 1 && readers_.get(Thread
106: .currentThread()) != null)) {
107: activeWriter_ = Thread.currentThread();
108: writeHolds_ = 1;
109: return true;
110: } else
111: return false;
112: } else
113: return false;
114: }
115:
116: protected synchronized Signaller endRead() {
117: Thread t = Thread.currentThread();
118: Object c = readers_.get(t);
119: if (c == null)
120: throw new IllegalStateException();
121: --activeReaders_;
122: if (c != IONE) { // more than one hold; decrement count
123: int h = ((Integer) (c)).intValue() - 1;
124: Integer ih = (h == 1) ? IONE : new Integer(h);
125: readers_.put(t, ih);
126: return null;
127: } else {
128: readers_.remove(t);
129:
130: if (writeHolds_ > 0) // a write lock is still held by current thread
131: return null;
132: else if (activeReaders_ == 0 && waitingWriters_ > 0)
133: return writerLock_;
134: else
135: return null;
136: }
137: }
138:
139: protected synchronized Signaller endWrite() {
140: --writeHolds_;
141: if (writeHolds_ > 0) // still being held
142: return null;
143: else {
144: activeWriter_ = null;
145: if (waitingReaders_ > 0 && allowReader())
146: return readerLock_;
147: else if (waitingWriters_ > 0)
148: return writerLock_;
149: else
150: return null;
151: }
152: }
153:
154: }
|