/* * File: ReentrantWriterPreferenceReadWriteLock.java Originally written by Doug * Lea and released into the public domain. This may be used for any purposes * whatsoever without acknowledgment. Thanks for the assistance and support of * Sun Microsystems Labs, and everyone contributing, testing, and using this * code. History: Date Who What 26aug1998 dl Create public version 7sep2000 dl * Readers are now also reentrant 19jan2001 dl Allow read->write upgrades if the * only reader 10dec2002 dl Throw IllegalStateException on extra release */ package org.castor.core.util.concurrent; import java.util.HashMap; /** * A writer-preference ReadWriteLock that allows both readers and writers to * reacquire read or write locks in the style of a ReentrantLock. Readers are * not allowed until all write locks held by the writing thread have been * released. Among other applications, reentrancy can be useful when write locks * are held during calls or callbacks to methods that perform reads under read * locks. * <p> * <b>Sample usage </b>. Here is a code sketch showing how to exploit reentrancy * to perform lock downgrading after updating a cache: * * <pre> * * class CachedData { * Object data; * volatile boolean cacheValid; * ReentrantWriterPreferenceReadWriteLock rwl = ... * * void processCachedData() { * rwl.readLock().acquire(); * if (!cacheValid) { * * // upgrade lock: * rwl.readLock().release(); // must release first to obtain writelock * rwl.writeLock().acquire(); * if (!cacheValid) { // recheck * data = ... * cacheValid = true; * } * // downgrade lock * rwl.readLock().acquire(); // reacquire read without giving up lock * rwl.writeLock().release(); // release write, still hold read * } * * use(data); * rwl.readLock().release(); * } * } * * </pre> * * <p>[ <a * href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> * Introduction to this package. </a>] * * @see "java.util.concurrent.locks.ReentrantLock in Java 5 or later JavaDoc" */ public class ReentrantWriterPreferenceReadWriteLock extends WriterPreferenceReadWriteLock { /** Number of acquires on write lock by activeWriter_ thread * */ protected long writeHolds_ = 0; /** Number of acquires on read lock by any reader thread * */ protected HashMap readers_ = new HashMap(); /** cache/reuse the special Integer value one to speed up readlocks * */ protected static final Integer IONE = new Integer(1); protected boolean allowReader() { return (activeWriter_ == null && waitingWriters_ == 0) || activeWriter_ == Thread.currentThread(); } protected synchronized boolean startRead() { Thread t = Thread.currentThread(); Object c = readers_.get(t); if(c != null) { // already held -- just increment hold count readers_.put(t, new Integer(((Integer)(c)).intValue() + 1)); ++activeReaders_; return true; } else if(allowReader()) { readers_.put(t, IONE); ++activeReaders_; return true; } else return false; } protected synchronized boolean startWrite() { if(activeWriter_ == Thread.currentThread()) { // already held; // re-acquire ++writeHolds_; return true; } else if(writeHolds_ == 0) { if(activeReaders_ == 0 || (readers_.size() == 1 && readers_.get(Thread .currentThread()) != null)) { activeWriter_ = Thread.currentThread(); writeHolds_ = 1; return true; } return false; } else return false; } protected synchronized Signaller endRead() { Thread t = Thread.currentThread(); Object c = readers_.get(t); if(c == null) throw new IllegalStateException(); --activeReaders_; if(c != IONE) { // more than one hold; decrement count int h = ((Integer)(c)).intValue() - 1; Integer ih = (h == 1) ? IONE : new Integer(h); readers_.put(t, ih); return null; } readers_.remove(t); if(writeHolds_ > 0) // a write lock is still held by current thread return null; else if(activeReaders_ == 0 && waitingWriters_ > 0) return writerLock_; else return null; } protected synchronized Signaller endWrite() { --writeHolds_; if(writeHolds_ > 0) // still being held return null; activeWriter_ = null; if(waitingReaders_ > 0 && allowReader()) return readerLock_; else if(waitingWriters_ > 0) return writerLock_; else return null; } }