001: /*
002: * Copyright 2002 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: ReadWriteLock.java,v 1.3 2002/11/08 05:06:27 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo.util;
012:
013: import org.apache.log4j.Category;
014:
015: /**
016: * A simple read-write lock implementation. Multiple threads may lock using
017: * readLock(), only one can lock using writeLock(). The caller is responsible
018: * for coding a try-finally that ensures unlock() is called for every readLock()
019: * and writeLock() call.
020: *
021: * <p>A ReadWriteLock is recursive; with one exception, a thread can re-lock an
022: * object it already has locked. Multiple read locks can be acquired by the
023: * same thread, as can multiple write locks. The exception however is that a
024: * write lock cannot be acquired when a read lock is already held (to allow
025: * this would cause deadlocks).
026: *
027: * <p>Successive lock calls from the same thread must be matched by an
028: * equal number of unlock() calls.
029: *
030: * @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
031: * @version $Revision: 1.3 $
032: */
033:
034: public class ReadWriteLock {
035: private static final Category LOG = Category
036: .getInstance(ReadWriteLock.class);
037:
038: private static final int WAIT_LOG_INTERVAL = 5000;
039:
040: /** A count for each thread indicating the number of read locks it holds. */
041: private ThreadLocal readLocksByThread;
042:
043: /** The number of read locks held across all threads. */
044: private int readLocks;
045:
046: /** The number of write locks held (by writeLockedBy). */
047: private int writeLocks;
048:
049: /** The thread holding the write lock(s), if any. */
050: private Thread writeLockedBy;
051:
052: /**
053: * An object holding a per-thread read-lock count.
054: */
055:
056: private static class Count {
057: public int value = 0;
058: }
059:
060: /**
061: * Constructs read-write lock.
062: */
063:
064: public ReadWriteLock() {
065: readLocksByThread = new ThreadLocal() {
066: public Object initialValue() {
067: return new Count();
068: }
069: };
070:
071: readLocks = 0;
072: writeLocks = 0;
073: writeLockedBy = null;
074: }
075:
076: /**
077: * Acquire a read lock. The calling thread will be suspended until no other
078: * thread holds a write lock.
079: *
080: * <p>If the calling thread already owns a write lock for the object a read
081: * lock is immediately acquired.
082: *
083: * @exception InterruptedException
084: * If the thread is interrupted while attempting to acquire the lock.
085: */
086:
087: public synchronized void readLock() throws InterruptedException {
088: Thread me = Thread.currentThread();
089: Count myReadLocks = (Count) readLocksByThread.get();
090:
091: if (writeLockedBy != me) {
092: while (writeLocks > 0) {
093: wait(WAIT_LOG_INTERVAL);
094:
095: if (writeLocks > 0)
096: LOG.debug("Still waiting for read lock on " + this ,
097: new InterruptedException());
098: }
099: }
100:
101: ++readLocks;
102: ++myReadLocks.value;
103: }
104:
105: /**
106: * Acquire a write lock. The calling thread will be suspended until no
107: * other thread holds a read or write lock.
108: *
109: * <p>This method cannot be called if the thread already owns a read lock on
110: * the same ReadWriteLock object, otherwise an
111: * <code>IllegalStateException</code> is thrown.
112: *
113: * @exception IllegalStateException
114: * If the thread already holds a read lock on the same object.
115: * @exception InterruptedException
116: * If the thread is interrupted while attempting to acquire the lock.
117: */
118:
119: public synchronized void writeLock() throws InterruptedException {
120: Thread me = Thread.currentThread();
121: Count myReadLocks = (Count) readLocksByThread.get();
122:
123: if (myReadLocks.value > 0)
124: throw new IllegalStateException(
125: "Thread already holds a read lock");
126:
127: if (writeLockedBy != me) {
128: while (writeLocks > 0 || readLocks > 0) {
129: wait(WAIT_LOG_INTERVAL);
130:
131: if (writeLocks > 0 || readLocks > 0)
132: LOG.debug(
133: "Still waiting for write lock on " + this ,
134: new InterruptedException());
135: }
136:
137: writeLockedBy = me;
138: }
139:
140: ++writeLocks;
141: }
142:
143: /**
144: * Release a read or write lock. Must be called in a finally block after
145: * acquiring a lock.
146: */
147:
148: public synchronized void unlock() {
149: Thread me = Thread.currentThread();
150: Count myReadLocks = (Count) readLocksByThread.get();
151:
152: if (myReadLocks.value > 0) {
153: --myReadLocks.value;
154: --readLocks;
155: } else if (writeLockedBy == me) {
156: if (writeLocks > 0) {
157: if (--writeLocks == 0)
158: writeLockedBy = null;
159: }
160: }
161:
162: notifyAll();
163: }
164:
165: public String toString() {
166: StringBuffer s = new StringBuffer(super .toString());
167:
168: s.append(": readLocks = ").append(readLocks).append(
169: ", writeLocks = ").append(writeLocks).append(
170: ", writeLockedBy = ").append(writeLockedBy);
171:
172: return s.toString();
173: }
174: }
|