jaroslav@1890: /* jaroslav@1890: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@1890: * jaroslav@1890: * This code is free software; you can redistribute it and/or modify it jaroslav@1890: * under the terms of the GNU General Public License version 2 only, as jaroslav@1890: * published by the Free Software Foundation. Oracle designates this jaroslav@1890: * particular file as subject to the "Classpath" exception as provided jaroslav@1890: * by Oracle in the LICENSE file that accompanied this code. jaroslav@1890: * jaroslav@1890: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@1890: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@1890: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@1890: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@1890: * accompanied this code). jaroslav@1890: * jaroslav@1890: * You should have received a copy of the GNU General Public License version jaroslav@1890: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@1890: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@1890: * jaroslav@1890: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@1890: * or visit www.oracle.com if you need additional information or have any jaroslav@1890: * questions. jaroslav@1890: */ jaroslav@1890: jaroslav@1890: /* jaroslav@1890: * This file is available under and governed by the GNU General Public jaroslav@1890: * License version 2 only, as published by the Free Software Foundation. jaroslav@1890: * However, the following notice accompanied the original version of this jaroslav@1890: * file: jaroslav@1890: * jaroslav@1890: * Written by Doug Lea with assistance from members of JCP JSR-166 jaroslav@1890: * Expert Group and released to the public domain, as explained at jaroslav@1890: * http://creativecommons.org/publicdomain/zero/1.0/ jaroslav@1890: */ jaroslav@1890: jaroslav@1890: package java.util.concurrent.locks; jaroslav@1890: import java.util.concurrent.*; jaroslav@1890: import java.util.concurrent.atomic.*; jaroslav@1890: import java.util.*; jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * An implementation of {@link ReadWriteLock} supporting similar jaroslav@1890: * semantics to {@link ReentrantLock}. jaroslav@1890: *

This class has the following properties: jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: *

Serialization of this class behaves in the same way as built-in jaroslav@1890: * locks: a deserialized lock is in the unlocked state, regardless of jaroslav@1890: * its state when serialized. jaroslav@1890: * jaroslav@1890: *

Sample usages. Here is a code sketch showing how to perform jaroslav@1890: * lock downgrading after updating a cache (exception handling is jaroslav@1890: * particularly tricky when handling multiple locks in a non-nested jaroslav@1890: * fashion): jaroslav@1890: * jaroslav@1890: *

 {@code
jaroslav@1890:  * class CachedData {
jaroslav@1890:  *   Object data;
jaroslav@1890:  *   volatile boolean cacheValid;
jaroslav@1890:  *   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
jaroslav@1890:  *
jaroslav@1890:  *   void processCachedData() {
jaroslav@1890:  *     rwl.readLock().lock();
jaroslav@1890:  *     if (!cacheValid) {
jaroslav@1890:  *        // Must release read lock before acquiring write lock
jaroslav@1890:  *        rwl.readLock().unlock();
jaroslav@1890:  *        rwl.writeLock().lock();
jaroslav@1890:  *        try {
jaroslav@1890:  *          // Recheck state because another thread might have
jaroslav@1890:  *          // acquired write lock and changed state before we did.
jaroslav@1890:  *          if (!cacheValid) {
jaroslav@1890:  *            data = ...
jaroslav@1890:  *            cacheValid = true;
jaroslav@1890:  *          }
jaroslav@1890:  *          // Downgrade by acquiring read lock before releasing write lock
jaroslav@1890:  *          rwl.readLock().lock();
jaroslav@1890:  *        } finally {
jaroslav@1890:  *          rwl.writeLock().unlock(); // Unlock write, still hold read
jaroslav@1890:  *        }
jaroslav@1890:  *     }
jaroslav@1890:  *
jaroslav@1890:  *     try {
jaroslav@1890:  *       use(data);
jaroslav@1890:  *     } finally {
jaroslav@1890:  *       rwl.readLock().unlock();
jaroslav@1890:  *     }
jaroslav@1890:  *   }
jaroslav@1890:  * }}
jaroslav@1890: * jaroslav@1890: * ReentrantReadWriteLocks can be used to improve concurrency in some jaroslav@1890: * uses of some kinds of Collections. This is typically worthwhile jaroslav@1890: * only when the collections are expected to be large, accessed by jaroslav@1890: * more reader threads than writer threads, and entail operations with jaroslav@1890: * overhead that outweighs synchronization overhead. For example, here jaroslav@1890: * is a class using a TreeMap that is expected to be large and jaroslav@1890: * concurrently accessed. jaroslav@1890: * jaroslav@1890: *
{@code
jaroslav@1890:  * class RWDictionary {
jaroslav@1890:  *    private final Map m = new TreeMap();
jaroslav@1890:  *    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
jaroslav@1890:  *    private final Lock r = rwl.readLock();
jaroslav@1890:  *    private final Lock w = rwl.writeLock();
jaroslav@1890:  *
jaroslav@1890:  *    public Data get(String key) {
jaroslav@1890:  *        r.lock();
jaroslav@1890:  *        try { return m.get(key); }
jaroslav@1890:  *        finally { r.unlock(); }
jaroslav@1890:  *    }
jaroslav@1890:  *    public String[] allKeys() {
jaroslav@1890:  *        r.lock();
jaroslav@1890:  *        try { return m.keySet().toArray(); }
jaroslav@1890:  *        finally { r.unlock(); }
jaroslav@1890:  *    }
jaroslav@1890:  *    public Data put(String key, Data value) {
jaroslav@1890:  *        w.lock();
jaroslav@1890:  *        try { return m.put(key, value); }
jaroslav@1890:  *        finally { w.unlock(); }
jaroslav@1890:  *    }
jaroslav@1890:  *    public void clear() {
jaroslav@1890:  *        w.lock();
jaroslav@1890:  *        try { m.clear(); }
jaroslav@1890:  *        finally { w.unlock(); }
jaroslav@1890:  *    }
jaroslav@1890:  * }}
jaroslav@1890: * jaroslav@1890: *

Implementation Notes

jaroslav@1890: * jaroslav@1890: *

This lock supports a maximum of 65535 recursive write locks jaroslav@1890: * and 65535 read locks. Attempts to exceed these limits result in jaroslav@1890: * {@link Error} throws from locking methods. jaroslav@1890: * jaroslav@1890: * @since 1.5 jaroslav@1890: * @author Doug Lea jaroslav@1890: * jaroslav@1890: */ jaroslav@1890: public class ReentrantReadWriteLock jaroslav@1890: implements ReadWriteLock, java.io.Serializable { jaroslav@1890: private static final long serialVersionUID = -6992448646407690164L; jaroslav@1890: /** Inner class providing readlock */ jaroslav@1890: private final ReentrantReadWriteLock.ReadLock readerLock; jaroslav@1890: /** Inner class providing writelock */ jaroslav@1890: private final ReentrantReadWriteLock.WriteLock writerLock; jaroslav@1890: /** Performs all synchronization mechanics */ jaroslav@1890: final Sync sync; jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Creates a new {@code ReentrantReadWriteLock} with jaroslav@1890: * default (nonfair) ordering properties. jaroslav@1890: */ jaroslav@1890: public ReentrantReadWriteLock() { jaroslav@1890: this(false); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Creates a new {@code ReentrantReadWriteLock} with jaroslav@1890: * the given fairness policy. jaroslav@1890: * jaroslav@1890: * @param fair {@code true} if this lock should use a fair ordering policy jaroslav@1890: */ jaroslav@1890: public ReentrantReadWriteLock(boolean fair) { jaroslav@1890: sync = fair ? new FairSync() : new NonfairSync(); jaroslav@1890: readerLock = new ReadLock(this); jaroslav@1890: writerLock = new WriteLock(this); jaroslav@1890: } jaroslav@1890: jaroslav@1890: public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; } jaroslav@1890: public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Synchronization implementation for ReentrantReadWriteLock. jaroslav@1890: * Subclassed into fair and nonfair versions. jaroslav@1890: */ jaroslav@1890: abstract static class Sync extends AbstractQueuedSynchronizer { jaroslav@1890: private static final long serialVersionUID = 6317671515068378041L; jaroslav@1890: jaroslav@1890: /* jaroslav@1890: * Read vs write count extraction constants and functions. jaroslav@1890: * Lock state is logically divided into two unsigned shorts: jaroslav@1890: * The lower one representing the exclusive (writer) lock hold count, jaroslav@1890: * and the upper the shared (reader) hold count. jaroslav@1890: */ jaroslav@1890: jaroslav@1890: static final int SHARED_SHIFT = 16; jaroslav@1890: static final int SHARED_UNIT = (1 << SHARED_SHIFT); jaroslav@1890: static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; jaroslav@1890: static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; jaroslav@1890: jaroslav@1890: /** Returns the number of shared holds represented in count */ jaroslav@1890: static int sharedCount(int c) { return c >>> SHARED_SHIFT; } jaroslav@1890: /** Returns the number of exclusive holds represented in count */ jaroslav@1890: static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * A counter for per-thread read hold counts. jaroslav@1890: * Maintained as a ThreadLocal; cached in cachedHoldCounter jaroslav@1890: */ jaroslav@1890: static final class HoldCounter { jaroslav@1890: int count = 0; jaroslav@1890: // Use id, not reference, to avoid garbage retention jaroslav@1890: final long tid = Thread.currentThread().getId(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * ThreadLocal subclass. Easiest to explicitly define for sake jaroslav@1890: * of deserialization mechanics. jaroslav@1890: */ jaroslav@1890: static final class ThreadLocalHoldCounter jaroslav@1890: extends ThreadLocal { jaroslav@1890: public HoldCounter initialValue() { jaroslav@1890: return new HoldCounter(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * The number of reentrant read locks held by current thread. jaroslav@1890: * Initialized only in constructor and readObject. jaroslav@1890: * Removed whenever a thread's read hold count drops to 0. jaroslav@1890: */ jaroslav@1890: private transient ThreadLocalHoldCounter readHolds; jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * The hold count of the last thread to successfully acquire jaroslav@1890: * readLock. This saves ThreadLocal lookup in the common case jaroslav@1890: * where the next thread to release is the last one to jaroslav@1890: * acquire. This is non-volatile since it is just used jaroslav@1890: * as a heuristic, and would be great for threads to cache. jaroslav@1890: * jaroslav@1890: *

Can outlive the Thread for which it is caching the read jaroslav@1890: * hold count, but avoids garbage retention by not retaining a jaroslav@1890: * reference to the Thread. jaroslav@1890: * jaroslav@1890: *

Accessed via a benign data race; relies on the memory jaroslav@1890: * model's final field and out-of-thin-air guarantees. jaroslav@1890: */ jaroslav@1890: private transient HoldCounter cachedHoldCounter; jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * firstReader is the first thread to have acquired the read lock. jaroslav@1890: * firstReaderHoldCount is firstReader's hold count. jaroslav@1890: * jaroslav@1890: *

More precisely, firstReader is the unique thread that last jaroslav@1890: * changed the shared count from 0 to 1, and has not released the jaroslav@1890: * read lock since then; null if there is no such thread. jaroslav@1890: * jaroslav@1890: *

Cannot cause garbage retention unless the thread terminated jaroslav@1890: * without relinquishing its read locks, since tryReleaseShared jaroslav@1890: * sets it to null. jaroslav@1890: * jaroslav@1890: *

Accessed via a benign data race; relies on the memory jaroslav@1890: * model's out-of-thin-air guarantees for references. jaroslav@1890: * jaroslav@1890: *

This allows tracking of read holds for uncontended read jaroslav@1890: * locks to be very cheap. jaroslav@1890: */ jaroslav@1890: private transient Thread firstReader = null; jaroslav@1890: private transient int firstReaderHoldCount; jaroslav@1890: jaroslav@1890: Sync() { jaroslav@1890: readHolds = new ThreadLocalHoldCounter(); jaroslav@1890: setState(getState()); // ensures visibility of readHolds jaroslav@1890: } jaroslav@1890: jaroslav@1890: /* jaroslav@1890: * Acquires and releases use the same code for fair and jaroslav@1890: * nonfair locks, but differ in whether/how they allow barging jaroslav@1890: * when queues are non-empty. jaroslav@1890: */ jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns true if the current thread, when trying to acquire jaroslav@1890: * the read lock, and otherwise eligible to do so, should block jaroslav@1890: * because of policy for overtaking other waiting threads. jaroslav@1890: */ jaroslav@1890: abstract boolean readerShouldBlock(); jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns true if the current thread, when trying to acquire jaroslav@1890: * the write lock, and otherwise eligible to do so, should block jaroslav@1890: * because of policy for overtaking other waiting threads. jaroslav@1890: */ jaroslav@1890: abstract boolean writerShouldBlock(); jaroslav@1890: jaroslav@1890: /* jaroslav@1890: * Note that tryRelease and tryAcquire can be called by jaroslav@1890: * Conditions. So it is possible that their arguments contain jaroslav@1890: * both read and write holds that are all released during a jaroslav@1890: * condition wait and re-established in tryAcquire. jaroslav@1890: */ jaroslav@1890: jaroslav@1890: protected final boolean tryRelease(int releases) { jaroslav@1890: if (!isHeldExclusively()) jaroslav@1890: throw new IllegalMonitorStateException(); jaroslav@1890: int nextc = getState() - releases; jaroslav@1890: boolean free = exclusiveCount(nextc) == 0; jaroslav@1890: if (free) jaroslav@1890: setExclusiveOwnerThread(null); jaroslav@1890: setState(nextc); jaroslav@1890: return free; jaroslav@1890: } jaroslav@1890: jaroslav@1890: protected final boolean tryAcquire(int acquires) { jaroslav@1890: /* jaroslav@1890: * Walkthrough: jaroslav@1890: * 1. If read count nonzero or write count nonzero jaroslav@1890: * and owner is a different thread, fail. jaroslav@1890: * 2. If count would saturate, fail. (This can only jaroslav@1890: * happen if count is already nonzero.) jaroslav@1890: * 3. Otherwise, this thread is eligible for lock if jaroslav@1890: * it is either a reentrant acquire or jaroslav@1890: * queue policy allows it. If so, update state jaroslav@1890: * and set owner. jaroslav@1890: */ jaroslav@1890: Thread current = Thread.currentThread(); jaroslav@1890: int c = getState(); jaroslav@1890: int w = exclusiveCount(c); jaroslav@1890: if (c != 0) { jaroslav@1890: // (Note: if c != 0 and w == 0 then shared count != 0) jaroslav@1890: if (w == 0 || current != getExclusiveOwnerThread()) jaroslav@1890: return false; jaroslav@1890: if (w + exclusiveCount(acquires) > MAX_COUNT) jaroslav@1890: throw new Error("Maximum lock count exceeded"); jaroslav@1890: // Reentrant acquire jaroslav@1890: setState(c + acquires); jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: if (writerShouldBlock() || jaroslav@1890: !compareAndSetState(c, c + acquires)) jaroslav@1890: return false; jaroslav@1890: setExclusiveOwnerThread(current); jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: jaroslav@1890: protected final boolean tryReleaseShared(int unused) { jaroslav@1890: Thread current = Thread.currentThread(); jaroslav@1890: if (firstReader == current) { jaroslav@1890: // assert firstReaderHoldCount > 0; jaroslav@1890: if (firstReaderHoldCount == 1) jaroslav@1890: firstReader = null; jaroslav@1890: else jaroslav@1890: firstReaderHoldCount--; jaroslav@1890: } else { jaroslav@1890: HoldCounter rh = cachedHoldCounter; jaroslav@1890: if (rh == null || rh.tid != current.getId()) jaroslav@1890: rh = readHolds.get(); jaroslav@1890: int count = rh.count; jaroslav@1890: if (count <= 1) { jaroslav@1890: readHolds.remove(); jaroslav@1890: if (count <= 0) jaroslav@1890: throw unmatchedUnlockException(); jaroslav@1890: } jaroslav@1890: --rh.count; jaroslav@1890: } jaroslav@1890: for (;;) { jaroslav@1890: int c = getState(); jaroslav@1890: int nextc = c - SHARED_UNIT; jaroslav@1890: if (compareAndSetState(c, nextc)) jaroslav@1890: // Releasing the read lock has no effect on readers, jaroslav@1890: // but it may allow waiting writers to proceed if jaroslav@1890: // both read and write locks are now free. jaroslav@1890: return nextc == 0; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: private IllegalMonitorStateException unmatchedUnlockException() { jaroslav@1890: return new IllegalMonitorStateException( jaroslav@1890: "attempt to unlock read lock, not locked by current thread"); jaroslav@1890: } jaroslav@1890: jaroslav@1890: protected final int tryAcquireShared(int unused) { jaroslav@1890: /* jaroslav@1890: * Walkthrough: jaroslav@1890: * 1. If write lock held by another thread, fail. jaroslav@1890: * 2. Otherwise, this thread is eligible for jaroslav@1890: * lock wrt state, so ask if it should block jaroslav@1890: * because of queue policy. If not, try jaroslav@1890: * to grant by CASing state and updating count. jaroslav@1890: * Note that step does not check for reentrant jaroslav@1890: * acquires, which is postponed to full version jaroslav@1890: * to avoid having to check hold count in jaroslav@1890: * the more typical non-reentrant case. jaroslav@1890: * 3. If step 2 fails either because thread jaroslav@1890: * apparently not eligible or CAS fails or count jaroslav@1890: * saturated, chain to version with full retry loop. jaroslav@1890: */ jaroslav@1890: Thread current = Thread.currentThread(); jaroslav@1890: int c = getState(); jaroslav@1890: if (exclusiveCount(c) != 0 && jaroslav@1890: getExclusiveOwnerThread() != current) jaroslav@1890: return -1; jaroslav@1890: int r = sharedCount(c); jaroslav@1890: if (!readerShouldBlock() && jaroslav@1890: r < MAX_COUNT && jaroslav@1890: compareAndSetState(c, c + SHARED_UNIT)) { jaroslav@1890: if (r == 0) { jaroslav@1890: firstReader = current; jaroslav@1890: firstReaderHoldCount = 1; jaroslav@1890: } else if (firstReader == current) { jaroslav@1890: firstReaderHoldCount++; jaroslav@1890: } else { jaroslav@1890: HoldCounter rh = cachedHoldCounter; jaroslav@1890: if (rh == null || rh.tid != current.getId()) jaroslav@1890: cachedHoldCounter = rh = readHolds.get(); jaroslav@1890: else if (rh.count == 0) jaroslav@1890: readHolds.set(rh); jaroslav@1890: rh.count++; jaroslav@1890: } jaroslav@1890: return 1; jaroslav@1890: } jaroslav@1890: return fullTryAcquireShared(current); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Full version of acquire for reads, that handles CAS misses jaroslav@1890: * and reentrant reads not dealt with in tryAcquireShared. jaroslav@1890: */ jaroslav@1890: final int fullTryAcquireShared(Thread current) { jaroslav@1890: /* jaroslav@1890: * This code is in part redundant with that in jaroslav@1890: * tryAcquireShared but is simpler overall by not jaroslav@1890: * complicating tryAcquireShared with interactions between jaroslav@1890: * retries and lazily reading hold counts. jaroslav@1890: */ jaroslav@1890: HoldCounter rh = null; jaroslav@1890: for (;;) { jaroslav@1890: int c = getState(); jaroslav@1890: if (exclusiveCount(c) != 0) { jaroslav@1890: if (getExclusiveOwnerThread() != current) jaroslav@1890: return -1; jaroslav@1890: // else we hold the exclusive lock; blocking here jaroslav@1890: // would cause deadlock. jaroslav@1890: } else if (readerShouldBlock()) { jaroslav@1890: // Make sure we're not acquiring read lock reentrantly jaroslav@1890: if (firstReader == current) { jaroslav@1890: // assert firstReaderHoldCount > 0; jaroslav@1890: } else { jaroslav@1890: if (rh == null) { jaroslav@1890: rh = cachedHoldCounter; jaroslav@1890: if (rh == null || rh.tid != current.getId()) { jaroslav@1890: rh = readHolds.get(); jaroslav@1890: if (rh.count == 0) jaroslav@1890: readHolds.remove(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: if (rh.count == 0) jaroslav@1890: return -1; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: if (sharedCount(c) == MAX_COUNT) jaroslav@1890: throw new Error("Maximum lock count exceeded"); jaroslav@1890: if (compareAndSetState(c, c + SHARED_UNIT)) { jaroslav@1890: if (sharedCount(c) == 0) { jaroslav@1890: firstReader = current; jaroslav@1890: firstReaderHoldCount = 1; jaroslav@1890: } else if (firstReader == current) { jaroslav@1890: firstReaderHoldCount++; jaroslav@1890: } else { jaroslav@1890: if (rh == null) jaroslav@1890: rh = cachedHoldCounter; jaroslav@1890: if (rh == null || rh.tid != current.getId()) jaroslav@1890: rh = readHolds.get(); jaroslav@1890: else if (rh.count == 0) jaroslav@1890: readHolds.set(rh); jaroslav@1890: rh.count++; jaroslav@1890: cachedHoldCounter = rh; // cache for release jaroslav@1890: } jaroslav@1890: return 1; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Performs tryLock for write, enabling barging in both modes. jaroslav@1890: * This is identical in effect to tryAcquire except for lack jaroslav@1890: * of calls to writerShouldBlock. jaroslav@1890: */ jaroslav@1890: final boolean tryWriteLock() { jaroslav@1890: Thread current = Thread.currentThread(); jaroslav@1890: int c = getState(); jaroslav@1890: if (c != 0) { jaroslav@1890: int w = exclusiveCount(c); jaroslav@1890: if (w == 0 || current != getExclusiveOwnerThread()) jaroslav@1890: return false; jaroslav@1890: if (w == MAX_COUNT) jaroslav@1890: throw new Error("Maximum lock count exceeded"); jaroslav@1890: } jaroslav@1890: if (!compareAndSetState(c, c + 1)) jaroslav@1890: return false; jaroslav@1890: setExclusiveOwnerThread(current); jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Performs tryLock for read, enabling barging in both modes. jaroslav@1890: * This is identical in effect to tryAcquireShared except for jaroslav@1890: * lack of calls to readerShouldBlock. jaroslav@1890: */ jaroslav@1890: final boolean tryReadLock() { jaroslav@1890: Thread current = Thread.currentThread(); jaroslav@1890: for (;;) { jaroslav@1890: int c = getState(); jaroslav@1890: if (exclusiveCount(c) != 0 && jaroslav@1890: getExclusiveOwnerThread() != current) jaroslav@1890: return false; jaroslav@1890: int r = sharedCount(c); jaroslav@1890: if (r == MAX_COUNT) jaroslav@1890: throw new Error("Maximum lock count exceeded"); jaroslav@1890: if (compareAndSetState(c, c + SHARED_UNIT)) { jaroslav@1890: if (r == 0) { jaroslav@1890: firstReader = current; jaroslav@1890: firstReaderHoldCount = 1; jaroslav@1890: } else if (firstReader == current) { jaroslav@1890: firstReaderHoldCount++; jaroslav@1890: } else { jaroslav@1890: HoldCounter rh = cachedHoldCounter; jaroslav@1890: if (rh == null || rh.tid != current.getId()) jaroslav@1890: cachedHoldCounter = rh = readHolds.get(); jaroslav@1890: else if (rh.count == 0) jaroslav@1890: readHolds.set(rh); jaroslav@1890: rh.count++; jaroslav@1890: } jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: protected final boolean isHeldExclusively() { jaroslav@1890: // While we must in general read state before owner, jaroslav@1890: // we don't need to do so to check if current thread is owner jaroslav@1890: return getExclusiveOwnerThread() == Thread.currentThread(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: // Methods relayed to outer class jaroslav@1890: jaroslav@1890: final ConditionObject newCondition() { jaroslav@1890: return new ConditionObject(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: final Thread getOwner() { jaroslav@1890: // Must read state before owner to ensure memory consistency jaroslav@1890: return ((exclusiveCount(getState()) == 0) ? jaroslav@1890: null : jaroslav@1890: getExclusiveOwnerThread()); jaroslav@1890: } jaroslav@1890: jaroslav@1890: final int getReadLockCount() { jaroslav@1890: return sharedCount(getState()); jaroslav@1890: } jaroslav@1890: jaroslav@1890: final boolean isWriteLocked() { jaroslav@1890: return exclusiveCount(getState()) != 0; jaroslav@1890: } jaroslav@1890: jaroslav@1890: final int getWriteHoldCount() { jaroslav@1890: return isHeldExclusively() ? exclusiveCount(getState()) : 0; jaroslav@1890: } jaroslav@1890: jaroslav@1890: final int getReadHoldCount() { jaroslav@1890: if (getReadLockCount() == 0) jaroslav@1890: return 0; jaroslav@1890: jaroslav@1890: Thread current = Thread.currentThread(); jaroslav@1890: if (firstReader == current) jaroslav@1890: return firstReaderHoldCount; jaroslav@1890: jaroslav@1890: HoldCounter rh = cachedHoldCounter; jaroslav@1890: if (rh != null && rh.tid == current.getId()) jaroslav@1890: return rh.count; jaroslav@1890: jaroslav@1890: int count = readHolds.get().count; jaroslav@1890: if (count == 0) readHolds.remove(); jaroslav@1890: return count; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Reconstitute this lock instance from a stream jaroslav@1890: * @param s the stream jaroslav@1890: */ jaroslav@1890: private void readObject(java.io.ObjectInputStream s) jaroslav@1890: throws java.io.IOException, ClassNotFoundException { jaroslav@1890: s.defaultReadObject(); jaroslav@1890: readHolds = new ThreadLocalHoldCounter(); jaroslav@1890: setState(0); // reset to unlocked state jaroslav@1890: } jaroslav@1890: jaroslav@1890: final int getCount() { return getState(); } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Nonfair version of Sync jaroslav@1890: */ jaroslav@1890: static final class NonfairSync extends Sync { jaroslav@1890: private static final long serialVersionUID = -8159625535654395037L; jaroslav@1890: final boolean writerShouldBlock() { jaroslav@1890: return false; // writers can always barge jaroslav@1890: } jaroslav@1890: final boolean readerShouldBlock() { jaroslav@1890: /* As a heuristic to avoid indefinite writer starvation, jaroslav@1890: * block if the thread that momentarily appears to be head jaroslav@1890: * of queue, if one exists, is a waiting writer. This is jaroslav@1890: * only a probabilistic effect since a new reader will not jaroslav@1890: * block if there is a waiting writer behind other enabled jaroslav@1890: * readers that have not yet drained from the queue. jaroslav@1890: */ jaroslav@1890: return apparentlyFirstQueuedIsExclusive(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Fair version of Sync jaroslav@1890: */ jaroslav@1890: static final class FairSync extends Sync { jaroslav@1890: private static final long serialVersionUID = -2274990926593161451L; jaroslav@1890: final boolean writerShouldBlock() { jaroslav@1890: return hasQueuedPredecessors(); jaroslav@1890: } jaroslav@1890: final boolean readerShouldBlock() { jaroslav@1890: return hasQueuedPredecessors(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * The lock returned by method {@link ReentrantReadWriteLock#readLock}. jaroslav@1890: */ jaroslav@1890: public static class ReadLock implements Lock, java.io.Serializable { jaroslav@1890: private static final long serialVersionUID = -5992448646407690164L; jaroslav@1890: private final Sync sync; jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Constructor for use by subclasses jaroslav@1890: * jaroslav@1890: * @param lock the outer lock object jaroslav@1890: * @throws NullPointerException if the lock is null jaroslav@1890: */ jaroslav@1890: protected ReadLock(ReentrantReadWriteLock lock) { jaroslav@1890: sync = lock.sync; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Acquires the read lock. jaroslav@1890: * jaroslav@1890: *

Acquires the read lock if the write lock is not held by jaroslav@1890: * another thread and returns immediately. jaroslav@1890: * jaroslav@1890: *

If the write lock is held by another thread then jaroslav@1890: * the current thread becomes disabled for thread scheduling jaroslav@1890: * purposes and lies dormant until the read lock has been acquired. jaroslav@1890: */ jaroslav@1890: public void lock() { jaroslav@1890: sync.acquireShared(1); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Acquires the read lock unless the current thread is jaroslav@1890: * {@linkplain Thread#interrupt interrupted}. jaroslav@1890: * jaroslav@1890: *

Acquires the read lock if the write lock is not held jaroslav@1890: * by another thread and returns immediately. jaroslav@1890: * jaroslav@1890: *

If the write lock is held by another thread then the jaroslav@1890: * current thread becomes disabled for thread scheduling jaroslav@1890: * purposes and lies dormant until one of two things happens: jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: *

If the current thread: jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: * then {@link InterruptedException} is thrown and the current jaroslav@1890: * thread's interrupted status is cleared. jaroslav@1890: * jaroslav@1890: *

In this implementation, as this method is an explicit jaroslav@1890: * interruption point, preference is given to responding to jaroslav@1890: * the interrupt over normal or reentrant acquisition of the jaroslav@1890: * lock. jaroslav@1890: * jaroslav@1890: * @throws InterruptedException if the current thread is interrupted jaroslav@1890: */ jaroslav@1890: public void lockInterruptibly() throws InterruptedException { jaroslav@1890: sync.acquireSharedInterruptibly(1); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Acquires the read lock only if the write lock is not held by jaroslav@1890: * another thread at the time of invocation. jaroslav@1890: * jaroslav@1890: *

Acquires the read lock if the write lock is not held by jaroslav@1890: * another thread and returns immediately with the value jaroslav@1890: * {@code true}. Even when this lock has been set to use a jaroslav@1890: * fair ordering policy, a call to {@code tryLock()} jaroslav@1890: * will immediately acquire the read lock if it is jaroslav@1890: * available, whether or not other threads are currently jaroslav@1890: * waiting for the read lock. This "barging" behavior jaroslav@1890: * can be useful in certain circumstances, even though it jaroslav@1890: * breaks fairness. If you want to honor the fairness setting jaroslav@1890: * for this lock, then use {@link #tryLock(long, TimeUnit) jaroslav@1890: * tryLock(0, TimeUnit.SECONDS) } which is almost equivalent jaroslav@1890: * (it also detects interruption). jaroslav@1890: * jaroslav@1890: *

If the write lock is held by another thread then jaroslav@1890: * this method will return immediately with the value jaroslav@1890: * {@code false}. jaroslav@1890: * jaroslav@1890: * @return {@code true} if the read lock was acquired jaroslav@1890: */ jaroslav@1890: public boolean tryLock() { jaroslav@1890: return sync.tryReadLock(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Acquires the read lock if the write lock is not held by jaroslav@1890: * another thread within the given waiting time and the jaroslav@1890: * current thread has not been {@linkplain Thread#interrupt jaroslav@1890: * interrupted}. jaroslav@1890: * jaroslav@1890: *

Acquires the read lock if the write lock is not held by jaroslav@1890: * another thread and returns immediately with the value jaroslav@1890: * {@code true}. If this lock has been set to use a fair jaroslav@1890: * ordering policy then an available lock will not be jaroslav@1890: * acquired if any other threads are waiting for the jaroslav@1890: * lock. This is in contrast to the {@link #tryLock()} jaroslav@1890: * method. If you want a timed {@code tryLock} that does jaroslav@1890: * permit barging on a fair lock then combine the timed and jaroslav@1890: * un-timed forms together: jaroslav@1890: * jaroslav@1890: *

if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
jaroslav@1890:          * 
jaroslav@1890: * jaroslav@1890: *

If the write lock is held by another thread then the jaroslav@1890: * current thread becomes disabled for thread scheduling jaroslav@1890: * purposes and lies dormant until one of three things happens: jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: *

If the read lock is acquired then the value {@code true} is jaroslav@1890: * returned. jaroslav@1890: * jaroslav@1890: *

If the current thread: jaroslav@1890: * jaroslav@1890: *

then {@link InterruptedException} is thrown and the jaroslav@1890: * current thread's interrupted status is cleared. jaroslav@1890: * jaroslav@1890: *

If the specified waiting time elapses then the value jaroslav@1890: * {@code false} is returned. If the time is less than or jaroslav@1890: * equal to zero, the method will not wait at all. jaroslav@1890: * jaroslav@1890: *

In this implementation, as this method is an explicit jaroslav@1890: * interruption point, preference is given to responding to jaroslav@1890: * the interrupt over normal or reentrant acquisition of the jaroslav@1890: * lock, and over reporting the elapse of the waiting time. jaroslav@1890: * jaroslav@1890: * @param timeout the time to wait for the read lock jaroslav@1890: * @param unit the time unit of the timeout argument jaroslav@1890: * @return {@code true} if the read lock was acquired jaroslav@1890: * @throws InterruptedException if the current thread is interrupted jaroslav@1890: * @throws NullPointerException if the time unit is null jaroslav@1890: * jaroslav@1890: */ jaroslav@1890: public boolean tryLock(long timeout, TimeUnit unit) jaroslav@1890: throws InterruptedException { jaroslav@1890: return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Attempts to release this lock. jaroslav@1890: * jaroslav@1890: *

If the number of readers is now zero then the lock jaroslav@1890: * is made available for write lock attempts. jaroslav@1890: */ jaroslav@1890: public void unlock() { jaroslav@1890: sync.releaseShared(1); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Throws {@code UnsupportedOperationException} because jaroslav@1890: * {@code ReadLocks} do not support conditions. jaroslav@1890: * jaroslav@1890: * @throws UnsupportedOperationException always jaroslav@1890: */ jaroslav@1890: public Condition newCondition() { jaroslav@1890: throw new UnsupportedOperationException(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a string identifying this lock, as well as its lock state. jaroslav@1890: * The state, in brackets, includes the String {@code "Read locks ="} jaroslav@1890: * followed by the number of held read locks. jaroslav@1890: * jaroslav@1890: * @return a string identifying this lock, as well as its lock state jaroslav@1890: */ jaroslav@1890: public String toString() { jaroslav@1890: int r = sync.getReadLockCount(); jaroslav@1890: return super.toString() + jaroslav@1890: "[Read locks = " + r + "]"; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * The lock returned by method {@link ReentrantReadWriteLock#writeLock}. jaroslav@1890: */ jaroslav@1890: public static class WriteLock implements Lock, java.io.Serializable { jaroslav@1890: private static final long serialVersionUID = -4992448646407690164L; jaroslav@1890: private final Sync sync; jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Constructor for use by subclasses jaroslav@1890: * jaroslav@1890: * @param lock the outer lock object jaroslav@1890: * @throws NullPointerException if the lock is null jaroslav@1890: */ jaroslav@1890: protected WriteLock(ReentrantReadWriteLock lock) { jaroslav@1890: sync = lock.sync; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Acquires the write lock. jaroslav@1890: * jaroslav@1890: *

Acquires the write lock if neither the read nor write lock jaroslav@1890: * are held by another thread jaroslav@1890: * and returns immediately, setting the write lock hold count to jaroslav@1890: * one. jaroslav@1890: * jaroslav@1890: *

If the current thread already holds the write lock then the jaroslav@1890: * hold count is incremented by one and the method returns jaroslav@1890: * immediately. jaroslav@1890: * jaroslav@1890: *

If the lock is held by another thread then the current jaroslav@1890: * thread becomes disabled for thread scheduling purposes and jaroslav@1890: * lies dormant until the write lock has been acquired, at which jaroslav@1890: * time the write lock hold count is set to one. jaroslav@1890: */ jaroslav@1890: public void lock() { jaroslav@1890: sync.acquire(1); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Acquires the write lock unless the current thread is jaroslav@1890: * {@linkplain Thread#interrupt interrupted}. jaroslav@1890: * jaroslav@1890: *

Acquires the write lock if neither the read nor write lock jaroslav@1890: * are held by another thread jaroslav@1890: * and returns immediately, setting the write lock hold count to jaroslav@1890: * one. jaroslav@1890: * jaroslav@1890: *

If the current thread already holds this lock then the jaroslav@1890: * hold count is incremented by one and the method returns jaroslav@1890: * immediately. jaroslav@1890: * jaroslav@1890: *

If the lock is held by another thread then the current jaroslav@1890: * thread becomes disabled for thread scheduling purposes and jaroslav@1890: * lies dormant until one of two things happens: jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: *

If the write lock is acquired by the current thread then the jaroslav@1890: * lock hold count is set to one. jaroslav@1890: * jaroslav@1890: *

If the current thread: jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: * then {@link InterruptedException} is thrown and the current jaroslav@1890: * thread's interrupted status is cleared. jaroslav@1890: * jaroslav@1890: *

In this implementation, as this method is an explicit jaroslav@1890: * interruption point, preference is given to responding to jaroslav@1890: * the interrupt over normal or reentrant acquisition of the jaroslav@1890: * lock. jaroslav@1890: * jaroslav@1890: * @throws InterruptedException if the current thread is interrupted jaroslav@1890: */ jaroslav@1890: public void lockInterruptibly() throws InterruptedException { jaroslav@1890: sync.acquireInterruptibly(1); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Acquires the write lock only if it is not held by another thread jaroslav@1890: * at the time of invocation. jaroslav@1890: * jaroslav@1890: *

Acquires the write lock if neither the read nor write lock jaroslav@1890: * are held by another thread jaroslav@1890: * and returns immediately with the value {@code true}, jaroslav@1890: * setting the write lock hold count to one. Even when this lock has jaroslav@1890: * been set to use a fair ordering policy, a call to jaroslav@1890: * {@code tryLock()} will immediately acquire the jaroslav@1890: * lock if it is available, whether or not other threads are jaroslav@1890: * currently waiting for the write lock. This "barging" jaroslav@1890: * behavior can be useful in certain circumstances, even jaroslav@1890: * though it breaks fairness. If you want to honor the jaroslav@1890: * fairness setting for this lock, then use {@link jaroslav@1890: * #tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) } jaroslav@1890: * which is almost equivalent (it also detects interruption). jaroslav@1890: * jaroslav@1890: *

If the current thread already holds this lock then the jaroslav@1890: * hold count is incremented by one and the method returns jaroslav@1890: * {@code true}. jaroslav@1890: * jaroslav@1890: *

If the lock is held by another thread then this method jaroslav@1890: * will return immediately with the value {@code false}. jaroslav@1890: * jaroslav@1890: * @return {@code true} if the lock was free and was acquired jaroslav@1890: * by the current thread, or the write lock was already held jaroslav@1890: * by the current thread; and {@code false} otherwise. jaroslav@1890: */ jaroslav@1890: public boolean tryLock( ) { jaroslav@1890: return sync.tryWriteLock(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Acquires the write lock if it is not held by another thread jaroslav@1890: * within the given waiting time and the current thread has jaroslav@1890: * not been {@linkplain Thread#interrupt interrupted}. jaroslav@1890: * jaroslav@1890: *

Acquires the write lock if neither the read nor write lock jaroslav@1890: * are held by another thread jaroslav@1890: * and returns immediately with the value {@code true}, jaroslav@1890: * setting the write lock hold count to one. If this lock has been jaroslav@1890: * set to use a fair ordering policy then an available lock jaroslav@1890: * will not be acquired if any other threads are jaroslav@1890: * waiting for the write lock. This is in contrast to the {@link jaroslav@1890: * #tryLock()} method. If you want a timed {@code tryLock} jaroslav@1890: * that does permit barging on a fair lock then combine the jaroslav@1890: * timed and un-timed forms together: jaroslav@1890: * jaroslav@1890: *

if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
jaroslav@1890:          * 
jaroslav@1890: * jaroslav@1890: *

If the current thread already holds this lock then the jaroslav@1890: * hold count is incremented by one and the method returns jaroslav@1890: * {@code true}. jaroslav@1890: * jaroslav@1890: *

If the lock is held by another thread then the current jaroslav@1890: * thread becomes disabled for thread scheduling purposes and jaroslav@1890: * lies dormant until one of three things happens: jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: *

If the write lock is acquired then the value {@code true} is jaroslav@1890: * returned and the write lock hold count is set to one. jaroslav@1890: * jaroslav@1890: *

If the current thread: jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: * then {@link InterruptedException} is thrown and the current jaroslav@1890: * thread's interrupted status is cleared. jaroslav@1890: * jaroslav@1890: *

If the specified waiting time elapses then the value jaroslav@1890: * {@code false} is returned. If the time is less than or jaroslav@1890: * equal to zero, the method will not wait at all. jaroslav@1890: * jaroslav@1890: *

In this implementation, as this method is an explicit jaroslav@1890: * interruption point, preference is given to responding to jaroslav@1890: * the interrupt over normal or reentrant acquisition of the jaroslav@1890: * lock, and over reporting the elapse of the waiting time. jaroslav@1890: * jaroslav@1890: * @param timeout the time to wait for the write lock jaroslav@1890: * @param unit the time unit of the timeout argument jaroslav@1890: * jaroslav@1890: * @return {@code true} if the lock was free and was acquired jaroslav@1890: * by the current thread, or the write lock was already held by the jaroslav@1890: * current thread; and {@code false} if the waiting time jaroslav@1890: * elapsed before the lock could be acquired. jaroslav@1890: * jaroslav@1890: * @throws InterruptedException if the current thread is interrupted jaroslav@1890: * @throws NullPointerException if the time unit is null jaroslav@1890: * jaroslav@1890: */ jaroslav@1890: public boolean tryLock(long timeout, TimeUnit unit) jaroslav@1890: throws InterruptedException { jaroslav@1890: return sync.tryAcquireNanos(1, unit.toNanos(timeout)); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Attempts to release this lock. jaroslav@1890: * jaroslav@1890: *

If the current thread is the holder of this lock then jaroslav@1890: * the hold count is decremented. If the hold count is now jaroslav@1890: * zero then the lock is released. If the current thread is jaroslav@1890: * not the holder of this lock then {@link jaroslav@1890: * IllegalMonitorStateException} is thrown. jaroslav@1890: * jaroslav@1890: * @throws IllegalMonitorStateException if the current thread does not jaroslav@1890: * hold this lock. jaroslav@1890: */ jaroslav@1890: public void unlock() { jaroslav@1890: sync.release(1); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a {@link Condition} instance for use with this jaroslav@1890: * {@link Lock} instance. jaroslav@1890: *

The returned {@link Condition} instance supports the same jaroslav@1890: * usages as do the {@link Object} monitor methods ({@link jaroslav@1890: * Object#wait() wait}, {@link Object#notify notify}, and {@link jaroslav@1890: * Object#notifyAll notifyAll}) when used with the built-in jaroslav@1890: * monitor lock. jaroslav@1890: * jaroslav@1890: *

jaroslav@1890: * jaroslav@1890: * @return the Condition object jaroslav@1890: */ jaroslav@1890: public Condition newCondition() { jaroslav@1890: return sync.newCondition(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a string identifying this lock, as well as its lock jaroslav@1890: * state. The state, in brackets includes either the String jaroslav@1890: * {@code "Unlocked"} or the String {@code "Locked by"} jaroslav@1890: * followed by the {@linkplain Thread#getName name} of the owning thread. jaroslav@1890: * jaroslav@1890: * @return a string identifying this lock, as well as its lock state jaroslav@1890: */ jaroslav@1890: public String toString() { jaroslav@1890: Thread o = sync.getOwner(); jaroslav@1890: return super.toString() + ((o == null) ? jaroslav@1890: "[Unlocked]" : jaroslav@1890: "[Locked by thread " + o.getName() + "]"); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries if this write lock is held by the current thread. jaroslav@1890: * Identical in effect to {@link jaroslav@1890: * ReentrantReadWriteLock#isWriteLockedByCurrentThread}. jaroslav@1890: * jaroslav@1890: * @return {@code true} if the current thread holds this lock and jaroslav@1890: * {@code false} otherwise jaroslav@1890: * @since 1.6 jaroslav@1890: */ jaroslav@1890: public boolean isHeldByCurrentThread() { jaroslav@1890: return sync.isHeldExclusively(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries the number of holds on this write lock by the current jaroslav@1890: * thread. A thread has a hold on a lock for each lock action jaroslav@1890: * that is not matched by an unlock action. Identical in effect jaroslav@1890: * to {@link ReentrantReadWriteLock#getWriteHoldCount}. jaroslav@1890: * jaroslav@1890: * @return the number of holds on this lock by the current thread, jaroslav@1890: * or zero if this lock is not held by the current thread jaroslav@1890: * @since 1.6 jaroslav@1890: */ jaroslav@1890: public int getHoldCount() { jaroslav@1890: return sync.getWriteHoldCount(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: // Instrumentation and status jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns {@code true} if this lock has fairness set true. jaroslav@1890: * jaroslav@1890: * @return {@code true} if this lock has fairness set true jaroslav@1890: */ jaroslav@1890: public final boolean isFair() { jaroslav@1890: return sync instanceof FairSync; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns the thread that currently owns the write lock, or jaroslav@1890: * {@code null} if not owned. When this method is called by a jaroslav@1890: * thread that is not the owner, the return value reflects a jaroslav@1890: * best-effort approximation of current lock status. For example, jaroslav@1890: * the owner may be momentarily {@code null} even if there are jaroslav@1890: * threads trying to acquire the lock but have not yet done so. jaroslav@1890: * This method is designed to facilitate construction of jaroslav@1890: * subclasses that provide more extensive lock monitoring jaroslav@1890: * facilities. jaroslav@1890: * jaroslav@1890: * @return the owner, or {@code null} if not owned jaroslav@1890: */ jaroslav@1890: protected Thread getOwner() { jaroslav@1890: return sync.getOwner(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries the number of read locks held for this lock. This jaroslav@1890: * method is designed for use in monitoring system state, not for jaroslav@1890: * synchronization control. jaroslav@1890: * @return the number of read locks held. jaroslav@1890: */ jaroslav@1890: public int getReadLockCount() { jaroslav@1890: return sync.getReadLockCount(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries if the write lock is held by any thread. This method is jaroslav@1890: * designed for use in monitoring system state, not for jaroslav@1890: * synchronization control. jaroslav@1890: * jaroslav@1890: * @return {@code true} if any thread holds the write lock and jaroslav@1890: * {@code false} otherwise jaroslav@1890: */ jaroslav@1890: public boolean isWriteLocked() { jaroslav@1890: return sync.isWriteLocked(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries if the write lock is held by the current thread. jaroslav@1890: * jaroslav@1890: * @return {@code true} if the current thread holds the write lock and jaroslav@1890: * {@code false} otherwise jaroslav@1890: */ jaroslav@1890: public boolean isWriteLockedByCurrentThread() { jaroslav@1890: return sync.isHeldExclusively(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries the number of reentrant write holds on this lock by the jaroslav@1890: * current thread. A writer thread has a hold on a lock for jaroslav@1890: * each lock action that is not matched by an unlock action. jaroslav@1890: * jaroslav@1890: * @return the number of holds on the write lock by the current thread, jaroslav@1890: * or zero if the write lock is not held by the current thread jaroslav@1890: */ jaroslav@1890: public int getWriteHoldCount() { jaroslav@1890: return sync.getWriteHoldCount(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries the number of reentrant read holds on this lock by the jaroslav@1890: * current thread. A reader thread has a hold on a lock for jaroslav@1890: * each lock action that is not matched by an unlock action. jaroslav@1890: * jaroslav@1890: * @return the number of holds on the read lock by the current thread, jaroslav@1890: * or zero if the read lock is not held by the current thread jaroslav@1890: * @since 1.6 jaroslav@1890: */ jaroslav@1890: public int getReadHoldCount() { jaroslav@1890: return sync.getReadHoldCount(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a collection containing threads that may be waiting to jaroslav@1890: * acquire the write lock. Because the actual set of threads may jaroslav@1890: * change dynamically while constructing this result, the returned jaroslav@1890: * collection is only a best-effort estimate. The elements of the jaroslav@1890: * returned collection are in no particular order. This method is jaroslav@1890: * designed to facilitate construction of subclasses that provide jaroslav@1890: * more extensive lock monitoring facilities. jaroslav@1890: * jaroslav@1890: * @return the collection of threads jaroslav@1890: */ jaroslav@1890: protected Collection getQueuedWriterThreads() { jaroslav@1890: return sync.getExclusiveQueuedThreads(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a collection containing threads that may be waiting to jaroslav@1890: * acquire the read lock. Because the actual set of threads may jaroslav@1890: * change dynamically while constructing this result, the returned jaroslav@1890: * collection is only a best-effort estimate. The elements of the jaroslav@1890: * returned collection are in no particular order. This method is jaroslav@1890: * designed to facilitate construction of subclasses that provide jaroslav@1890: * more extensive lock monitoring facilities. jaroslav@1890: * jaroslav@1890: * @return the collection of threads jaroslav@1890: */ jaroslav@1890: protected Collection getQueuedReaderThreads() { jaroslav@1890: return sync.getSharedQueuedThreads(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries whether any threads are waiting to acquire the read or jaroslav@1890: * write lock. Note that because cancellations may occur at any jaroslav@1890: * time, a {@code true} return does not guarantee that any other jaroslav@1890: * thread will ever acquire a lock. This method is designed jaroslav@1890: * primarily for use in monitoring of the system state. jaroslav@1890: * jaroslav@1890: * @return {@code true} if there may be other threads waiting to jaroslav@1890: * acquire the lock jaroslav@1890: */ jaroslav@1890: public final boolean hasQueuedThreads() { jaroslav@1890: return sync.hasQueuedThreads(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries whether the given thread is waiting to acquire either jaroslav@1890: * the read or write lock. Note that because cancellations may jaroslav@1890: * occur at any time, a {@code true} return does not guarantee jaroslav@1890: * that this thread will ever acquire a lock. This method is jaroslav@1890: * designed primarily for use in monitoring of the system state. jaroslav@1890: * jaroslav@1890: * @param thread the thread jaroslav@1890: * @return {@code true} if the given thread is queued waiting for this lock jaroslav@1890: * @throws NullPointerException if the thread is null jaroslav@1890: */ jaroslav@1890: public final boolean hasQueuedThread(Thread thread) { jaroslav@1890: return sync.isQueued(thread); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns an estimate of the number of threads waiting to acquire jaroslav@1890: * either the read or write lock. The value is only an estimate jaroslav@1890: * because the number of threads may change dynamically while this jaroslav@1890: * method traverses internal data structures. This method is jaroslav@1890: * designed for use in monitoring of the system state, not for jaroslav@1890: * synchronization control. jaroslav@1890: * jaroslav@1890: * @return the estimated number of threads waiting for this lock jaroslav@1890: */ jaroslav@1890: public final int getQueueLength() { jaroslav@1890: return sync.getQueueLength(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a collection containing threads that may be waiting to jaroslav@1890: * acquire either the read or write lock. Because the actual set jaroslav@1890: * of threads may change dynamically while constructing this jaroslav@1890: * result, the returned collection is only a best-effort estimate. jaroslav@1890: * The elements of the returned collection are in no particular jaroslav@1890: * order. This method is designed to facilitate construction of jaroslav@1890: * subclasses that provide more extensive monitoring facilities. jaroslav@1890: * jaroslav@1890: * @return the collection of threads jaroslav@1890: */ jaroslav@1890: protected Collection getQueuedThreads() { jaroslav@1890: return sync.getQueuedThreads(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Queries whether any threads are waiting on the given condition jaroslav@1890: * associated with the write lock. Note that because timeouts and jaroslav@1890: * interrupts may occur at any time, a {@code true} return does jaroslav@1890: * not guarantee that a future {@code signal} will awaken any jaroslav@1890: * threads. This method is designed primarily for use in jaroslav@1890: * monitoring of the system state. jaroslav@1890: * jaroslav@1890: * @param condition the condition jaroslav@1890: * @return {@code true} if there are any waiting threads jaroslav@1890: * @throws IllegalMonitorStateException if this lock is not held jaroslav@1890: * @throws IllegalArgumentException if the given condition is jaroslav@1890: * not associated with this lock jaroslav@1890: * @throws NullPointerException if the condition is null jaroslav@1890: */ jaroslav@1890: public boolean hasWaiters(Condition condition) { jaroslav@1890: if (condition == null) jaroslav@1890: throw new NullPointerException(); jaroslav@1890: if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) jaroslav@1890: throw new IllegalArgumentException("not owner"); jaroslav@1890: return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns an estimate of the number of threads waiting on the jaroslav@1890: * given condition associated with the write lock. Note that because jaroslav@1890: * timeouts and interrupts may occur at any time, the estimate jaroslav@1890: * serves only as an upper bound on the actual number of waiters. jaroslav@1890: * This method is designed for use in monitoring of the system jaroslav@1890: * state, not for synchronization control. jaroslav@1890: * jaroslav@1890: * @param condition the condition jaroslav@1890: * @return the estimated number of waiting threads jaroslav@1890: * @throws IllegalMonitorStateException if this lock is not held jaroslav@1890: * @throws IllegalArgumentException if the given condition is jaroslav@1890: * not associated with this lock jaroslav@1890: * @throws NullPointerException if the condition is null jaroslav@1890: */ jaroslav@1890: public int getWaitQueueLength(Condition condition) { jaroslav@1890: if (condition == null) jaroslav@1890: throw new NullPointerException(); jaroslav@1890: if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) jaroslav@1890: throw new IllegalArgumentException("not owner"); jaroslav@1890: return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a collection containing those threads that may be jaroslav@1890: * waiting on the given condition associated with the write lock. jaroslav@1890: * Because the actual set of threads may change dynamically while jaroslav@1890: * constructing this result, the returned collection is only a jaroslav@1890: * best-effort estimate. The elements of the returned collection jaroslav@1890: * are in no particular order. This method is designed to jaroslav@1890: * facilitate construction of subclasses that provide more jaroslav@1890: * extensive condition monitoring facilities. jaroslav@1890: * jaroslav@1890: * @param condition the condition jaroslav@1890: * @return the collection of threads jaroslav@1890: * @throws IllegalMonitorStateException if this lock is not held jaroslav@1890: * @throws IllegalArgumentException if the given condition is jaroslav@1890: * not associated with this lock jaroslav@1890: * @throws NullPointerException if the condition is null jaroslav@1890: */ jaroslav@1890: protected Collection getWaitingThreads(Condition condition) { jaroslav@1890: if (condition == null) jaroslav@1890: throw new NullPointerException(); jaroslav@1890: if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) jaroslav@1890: throw new IllegalArgumentException("not owner"); jaroslav@1890: return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a string identifying this lock, as well as its lock state. jaroslav@1890: * The state, in brackets, includes the String {@code "Write locks ="} jaroslav@1890: * followed by the number of reentrantly held write locks, and the jaroslav@1890: * String {@code "Read locks ="} followed by the number of held jaroslav@1890: * read locks. jaroslav@1890: * jaroslav@1890: * @return a string identifying this lock, as well as its lock state jaroslav@1890: */ jaroslav@1890: public String toString() { jaroslav@1890: int c = sync.getCount(); jaroslav@1890: int w = Sync.exclusiveCount(c); jaroslav@1890: int r = Sync.sharedCount(c); jaroslav@1890: jaroslav@1890: return super.toString() + jaroslav@1890: "[Write locks = " + w + ", Read locks = " + r + "]"; jaroslav@1890: } jaroslav@1890: jaroslav@1890: }