ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ReentrantReadWriteLock.java
Revision: 1.4
Committed: Tue Jun 24 14:34:48 2003 UTC (20 years, 11 months ago) by dl
Branch: MAIN
Changes since 1.3: +29 -7 lines
Log Message:
Added missing javadoc tags; minor reformatting

File Contents

# User Rev Content
1 dl 1.2 /*
2     * Written by Doug Lea with assistance from members of JCP JSR-166
3     * Expert Group and released to the public domain. Use, modify, and
4     * redistribute this code in any way without acknowledgement.
5     */
6    
7 tim 1.1 package java.util.concurrent;
8    
9     /**
10 dl 1.3 * An implementation of {@link ReadWriteLock} supporting similar
11     * semantics to {@link ReentrantLock}.
12 tim 1.1 * <p>This class has the following properties:
13     * <ul>
14     * <li><b>Acquisition order</b>
15     * <p> This class does not impose a reader or writer preference
16     * ordering for lock access. Instead, threads contend using an
17     * approximately arrival-order policy. The actual order depends on the
18 dl 1.3 * order in which an internal {@link FairReentrantLock} is granted, but
19 tim 1.1 * essentially when the write lock is released either the single writer
20     * at the notional head of the queue will be assigned the write lock, or
21     * the set of readers at the head of the queue will be assigned the read lock.
22     * <p>If readers are active and a writer arrives then no subsequent readers
23     * will be granted the read lock until after that writer has acquired and
24     * released the write lock.
25     *
26     * <li><b>Reentrancy</b>
27     * <p>This lock allows both readers and writers to reacquire read or
28     * write locks in the style of a {@link ReentrantLock}. Readers are not
29     * allowed until all write locks held by the writing thread have been
30     * released.
31     * <p>Additionally, a writer can acquire the read lock - but not vice-versa.
32     * Among other applications, reentrancy can be useful when
33     * write locks are held during calls or callbacks to methods that
34     * perform reads under read locks.
35     * If a reader tries to acquire the write lock it will never succeed.
36     *
37     * <li><b>Lock downgrading</b>
38     * <p>Reentrancy also allows downgrading from the write lock to a read lock,
39     * by acquiring the write lock, then the read lock and then releasing the
40 dl 1.3 * write lock. However, upgrading from a read lock to the write lock, is
41 tim 1.1 * <b>not</b> possible.
42     *
43     * <li><b>Interruption of lock aquisition</b>
44     * <p>Both the read lock and write lock support interruption during lock
45     * acquisition.
46     *
47     * <li><b>{@link Condition} support</b>
48     * <p>The write lock provides a {@link Condition} implementation that
49     * behaves in the same way, with respect to the write lock, as the
50     * {@link Condition} implementation provided by
51     * {@link ReentrantLock#newCondition} does for {@link ReentrantLock}.
52     * This {@link Condition} can, of course, only be used with the write lock.
53     * <p>The read lock does not support a {@link Condition} and
54     * <tt>readLock().newCondition()</tt> will return <tt>null</tt>.
55     *
56     * <li><b>Ownership</b>
57     * <p>While not exposing a means to query the owner, the write lock does
58     * internally define an owner and so the write lock can only be released by
59     * the thread that acquired it.
60 dl 1.3 * <p>In contrast, the read lock has no concept of ownership.
61     * Consequently, while not a particularly
62 tim 1.1 * good practice, it is possible to acquire a read lock in one thread, and
63     * release it in another. This can occasionally be useful.
64     *
65     * </ul>
66     *
67     * <p><b>Sample usage</b>. Here is a code sketch showing how to exploit
68     * reentrancy to perform lock downgrading after updating a cache (exception
69     * handling is elided for simplicity):
70     * <pre>
71     * class CachedData {
72     * Object data;
73     * volatile boolean cacheValid;
74     * ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
75     *
76     * void processCachedData() {
77     * rwl.readLock().lock();
78     * if (!cacheValid) {
79     * // upgrade lock manually
80     * rwl.readLock().unlock(); // must unlock first to obtain writelock
81     * rwl.writeLock().lock();
82     * if (!cacheValid) { // recheck
83     * data = ...
84     * cacheValid = true;
85     * }
86     * // downgrade lock
87     * rwl.readLock().lock(); // reacquire read without giving up write lock
88     * rwl.writeLock().unlock(); // unlock write, still hold read
89     * }
90     *
91     * use(data);
92     * rwl.readLock().unlock();
93     * }
94     * }
95     * </pre>
96     *
97     * @see ReentrantLock
98     * @see Condition
99     * @see ReadWriteLock
100     * @see Locks
101     *
102     * @since 1.5
103     * @spec JSR-166
104 dl 1.4 * @revised $Date: 2003/06/07 18:20:21 $
105 dl 1.3 * @editor $Author: dl $
106 dl 1.4 * @author Doug Lea
107 tim 1.1 *
108     */
109 dl 1.2 public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
110    
111     /*
112     Note that all fields are transient and defined in a way that
113     deserialized locks are in initial unlocked state.
114     */
115    
116     /** Inner class providing readlock */
117 dl 1.4 private final Lock readerLock = new ReaderLock();
118 dl 1.2 /** Inner class providing writelock */
119 dl 1.4 private final Lock writerLock = new WriterLock();
120 dl 1.2
121     public Lock writeLock() { return writerLock; }
122 dl 1.4
123 dl 1.2 public Lock readLock() { return readerLock; }
124    
125     /**
126     * Main lock. Writers acquire on entry, and hold until release.
127     * Reentrant acquires on write lock are allowed. Readers acquire
128     * and release only during entry, but are blocked from doing so if
129     * there is an active writer. RRWLock is a simple ReentrantLock
130     * subclass defined below that surrounds WriteLock condition waits
131     * with bookeeping to save and restore writer state.
132     **/
133 dl 1.4 private final RRWLock entryLock = new RRWLock();
134 dl 1.2
135     /**
136     * Number of threads that have entered read lock. This is never
137     * reset to zero. Incremented only during acquisition of read lock
138     * while the "entryLock" is held, but read elsewhere, so is declared
139     * volatile.
140     **/
141     private transient volatile int readers;
142    
143     /**
144     * Number of threads that have exited read lock. This is never
145     * reset to zero. Accessed only in code protected by rLock. When
146     * exreaders != readers, the rwlock is being used for
147     * reading. Else if the entry lock is held, it is being used for
148     * writing (or in transition). Else it is free. Note: To
149     * distinguish these states, we assume that fewer than 2^32 reader
150     * threads can simultaneously execute.
151     **/
152     private transient int exreaders;
153    
154     /**
155     * Flag set while writing. Needed by ReaderLock.tryLock to
156     * distinguish contention from unavailability. Accessed
157     * in ways that do not require being volatile.
158     */
159     private transient boolean writing;
160    
161     /**
162     * Lock used by exiting readers and entering writers
163     */
164    
165 dl 1.4 private final ReentrantLock writeCheckLock = new ReentrantLock();
166 dl 1.2
167     /**
168     * Condition waited on by blocked entering writers.
169     */
170 dl 1.4 private final Condition writeEnabled = writeCheckLock.newCondition();
171 dl 1.2
172     /**
173     * Reader exit protocol, called from unlock. if this is the last
174     * reader, notify a possibly waiting writer. Because waits occur
175     * only when entry lock is held, at most one writer can be waiting
176     * for this notification. Because increments to "readers" aren't
177     * protected by "this" lock, the notification may be spurious
178     * (when an incoming reader in in the process of updating the
179     * field), but at the point tested in acquiring write lock, both
180     * locks will be held, thus avoiding false alarms. And we will
181     * never miss an opportunity to send a notification when it is
182     * actually needed.
183     */
184     private void readerExit() {
185     writeCheckLock.lock();
186     try {
187     if (++exreaders == readers)
188     writeEnabled.signal();
189     }
190     finally {
191     writeCheckLock.unlock();
192     }
193     }
194    
195     /**
196     * Writer enter protocol, called after acquiring entry lock.
197     * Wait until all readers have exited.
198 dl 1.4 * @throws InterruptedException if interrupted while waiting
199 dl 1.2 */
200     private void writerEnter() throws InterruptedException {
201     writeCheckLock.lock();
202     try {
203     while (exreaders != readers)
204     writeEnabled.await();
205     }
206     finally {
207     writeCheckLock.unlock();
208     }
209 tim 1.1
210     }
211    
212 dl 1.4 /**
213     * Trylock version of Writer enter protocol
214     * @return true if successful
215     */
216 dl 1.2 private boolean tryWriterEnter() {
217     writeCheckLock.lock();
218     boolean ok = (exreaders == readers);
219     writeCheckLock.unlock();
220     return ok;
221     }
222    
223 dl 1.4 /**
224     * Timed version of writer enter protocol, called after acquiring
225     * entry lock. Wait until all readers have exited.
226     * @param nanos the wait time
227     * @return true if successful
228     * @throws InterruptedException if interrupted while waiting
229     */
230 dl 1.2 private boolean tryWriterEnter(long nanos) throws InterruptedException {
231     writeCheckLock.lock();
232     try {
233     while (exreaders != readers) {
234     if (nanos <= 0)
235     return false;
236     nanos = writeEnabled.awaitNanos(nanos);
237     }
238     return true;
239     }
240     finally {
241     writeCheckLock.unlock();
242     }
243     }
244    
245    
246 dl 1.4 /**
247     * The Reader lock
248     */
249 dl 1.2 private class ReaderLock implements Lock {
250    
251     public void lock() {
252     entryLock.lock();
253     ++readers;
254     entryLock.unlock();
255     }
256    
257     public void lockInterruptibly() throws InterruptedException {
258     entryLock.lockInterruptibly();
259     ++readers;
260     entryLock.unlock();
261     }
262    
263     public boolean tryLock() {
264     // if we fail entry lock just due to contention, try again.
265     while (!entryLock.tryLock()) {
266     if (writing)
267     return false;
268     else
269     Thread.yield();
270     }
271    
272     ++readers;
273     entryLock.unlock();
274     return true;
275     }
276    
277     public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
278     if (!entryLock.tryLock(time, unit))
279     return false;
280    
281     ++readers;
282     entryLock.unlock();
283     return true;
284     }
285    
286     public void unlock() {
287     readerExit();
288     }
289    
290     public Condition newCondition() {
291     throw new UnsupportedOperationException();
292     }
293     }
294    
295 dl 1.4 /**
296     * The writer lock
297     */
298 dl 1.2 private class WriterLock implements Lock {
299     public void lock() {
300     entryLock.lock();
301     writing = true;
302     if (entryLock.getHoldCount() > 1) // skip if held reentrantly
303     return;
304    
305     boolean wasInterrupted = false;
306     for (;;) {
307     try {
308     writerEnter();
309     break;
310     }
311     catch (InterruptedException ie) {
312     wasInterrupted = true;
313     }
314     }
315     if (wasInterrupted) {
316     Thread.currentThread().interrupt();
317     }
318     }
319    
320     public void lockInterruptibly() throws InterruptedException {
321     entryLock.lockInterruptibly();
322     writing = true;
323     if (entryLock.getHoldCount() > 1) // skip if held reentrantly
324     return;
325    
326     try {
327     writerEnter();
328     }
329     catch (InterruptedException ie) {
330     entryLock.unlock();
331     throw ie;
332     }
333     }
334    
335     public boolean tryLock( ) {
336     if (!entryLock.tryLock())
337     return false;
338     writing = true;
339     if (entryLock.getHoldCount() > 1)
340     return true;
341    
342     if (!tryWriterEnter()) {
343     entryLock.unlock();
344     writing = false;
345     return false;
346     }
347     return true;
348     }
349    
350     public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
351     long startTime = JSR166Support.currentTimeNanos();
352     long nanos = unit.toNanos(time);
353     if (!entryLock.tryLock(nanos, TimeUnit.NANOSECONDS))
354     return false;
355     writing = true;
356     if (entryLock.getHoldCount() > 1)
357     return true;
358    
359     nanos -= JSR166Support.currentTimeNanos() - startTime;
360     try {
361     if (!tryWriterEnter(nanos)) {
362     entryLock.unlock();
363     writing = false;
364     return false;
365     }
366     }
367     catch (InterruptedException ie) {
368     writing = false;
369     entryLock.unlock();
370    
371     throw ie;
372     }
373     return true;
374     }
375    
376     public void unlock() {
377     // must perform owner check before clearing "writing"
378 dl 1.4 if (!entryLock.isHeldByCurrentThread())
379     throw new IllegalMonitorStateException();
380 dl 1.2 writing = false;
381     entryLock.unlock();
382     }
383    
384     public Condition newCondition() {
385     return entryLock.newCondition();
386     }
387    
388 tim 1.1 }
389    
390 dl 1.2 /**
391     * Subclass of ReentrantLock to adjust fields on entry and exit to
392     * condition waits.
393     */
394     class RRWLock extends FairReentrantLock {
395    
396     void beforeWait() {
397     writing = false;
398     }
399 tim 1.1
400 dl 1.2 void afterWait() {
401     writing = true;
402     // same as lock, except never bypass writerEnter.
403     boolean wasInterrupted = false;
404     for (;;) {
405     try {
406     writerEnter();
407     break;
408     }
409     catch (InterruptedException ie) {
410     wasInterrupted = true;
411     }
412     }
413     if (wasInterrupted) {
414     Thread.currentThread().interrupt();
415     }
416     }
417     }
418 dl 1.4
419 dl 1.2 }
420 tim 1.1