/* * Copyright 2005 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wamblee.concurrency; import java.util.HashSet; /** * Read-write lock for allowing multiple concurrent readers or at most one * writer. This implementation does not aim for high performance but for * robustness and simplicity. Users of this class should not synchronize on * objects of this class. */ public class ReadWriteLock { /** * Sets containing the references to the threads that are currently * reading. This administration is useful to check that the lock has * already been acquired before it is release. This check adds robustness * to the application. */ private HashSet readers; /** * The thread that has acquired the lock for writing or null if no such * thread exists currently. */ private Thread writer; /** * Constructs read-write lock. */ public ReadWriteLock() { readers = new HashSet(); writer = null; } /** * Acquires the lock for reading. This call will block until the lock can * be acquired. * * @throws IllegalStateException Thrown if the read or write lock is * already acquired. */ public synchronized void acquireRead() { if (readers.contains(Thread.currentThread())) { throw new IllegalStateException( "Read lock already acquired by current thread: " + Thread.currentThread()); } if (writer == Thread.currentThread()) { throw new IllegalStateException( "Trying to acquire the read lock while already holding a write lock: " + Thread.currentThread()); } while (writer != null) { try { wait(); } catch (InterruptedException e) { notifyAll(); } } readers.add(Thread.currentThread()); } /** * Releases the lock for reading. Note: This implementation assumes that * the lock has already been acquired for reading previously. * * @throws IllegalStateException Thrown when the lock was not acquired by * this thread. */ public synchronized void releaseRead() { if (!readers.remove(Thread.currentThread())) { throw new IllegalStateException( "Cannot release read lock because current thread has not acquired it."); } if (readers.size() == 0) { notifyAll(); } } /** * Acquires the lock for writing. This call will block until the lock has * been acquired. * * @throws IllegalStateException Thrown if the read or write lock is * already acquired. */ public synchronized void acquireWrite() { if (writer == Thread.currentThread()) { throw new IllegalStateException( "Trying to acquire a write lock while already holding the write lock: " + Thread.currentThread()); } if (readers.contains(Thread.currentThread())) { throw new IllegalStateException( "Trying to acquire a write lock while already holding the read lock: " + Thread.currentThread()); } // wait until there are no more writers and no more // readers while ((writer != null) || (readers.size() > 0)) { try { wait(); } catch (InterruptedException e) { notifyAll(); } } writer = Thread.currentThread(); // notification not necessary since all writers and // readers are now blocked by this thread. } /** * Releases the lock for writing. * * @throws IllegalStateException Thrown when the lock was not acquired. */ public synchronized void releaseWrite() { if (writer != Thread.currentThread()) { throw new IllegalStateException( "Cannot release write lock because it was not acquired. "); } writer = null; notifyAll(); } }