001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.concurrent; 018 019import java.util.concurrent.locks.*; 020 021/** 022 * An extension of {@link ReentrantReadWriteLock} with convenience methods for creating 023 * auto-closeable locks that work seamlessly with try-with-resources statements. 024 * 025 * <p> 026 * This class provides a simpler API than the standard {@link ReentrantReadWriteLock} by wrapping 027 * read and write locks in {@link SimpleLock} instances that implement {@link AutoCloseable}. 028 * This allows locks to be automatically released when used in try-with-resources blocks, reducing 029 * the risk of forgetting to unlock and preventing deadlocks. 030 * 031 * <p> 032 * The returned {@link SimpleLock} instances automatically acquire the lock when created and release 033 * it when {@link SimpleLock#close()} is called (either explicitly or automatically via try-with-resources). 034 * 035 * <h5 class='section'>Example:</h5> 036 * <p class='bjava'> 037 * <jc>// Using read lock with try-with-resources</jc> 038 * <jk>var</jk> <jv>lock</jv> = <jk>new</jk> SimpleReadWriteLock(); 039 * <jk>try</jk> (<jk>var</jk> <jv>readLock</jv> = <jv>lock</jv>.read()) { 040 * <jc>// Read operations here - lock is automatically held</jc> 041 * <jv>sharedResource</jv>.read(); 042 * } 043 * <jc>// Lock is automatically released when exiting try block</jc> 044 * 045 * <jc>// Using write lock with try-with-resources</jc> 046 * <jk>try</jk> (<jk>var</jk> <jv>writeLock</jv> = <jv>lock</jv>.write()) { 047 * <jc>// Write operations here - lock is automatically held</jc> 048 * <jv>sharedResource</jv>.write(<js>"new value"</js>); 049 * } 050 * <jc>// Lock is automatically released when exiting try block</jc> 051 * </p> 052 * 053 * <h5 class='section'>Thread Safety:</h5> 054 * <p> 055 * This class inherits all thread-safety guarantees from {@link ReentrantReadWriteLock}. 056 * Multiple threads can hold read locks simultaneously, but write locks are exclusive. 057 * Read and write locks cannot be held simultaneously by different threads. 058 * 059 * <h5 class='section'>Notes:</h5><ul> 060 * <li class='note'>The returned {@link SimpleLock} instances are not thread-safe themselves and should 061 * not be shared between threads. Each thread should call {@link #read()} or {@link #write()} separately. 062 * <li class='note'>Each call to {@link #read()} or {@link #write()} returns a new {@link SimpleLock} instance. 063 * <li class='note'>The lock is automatically acquired when {@link SimpleLock} is created and released when 064 * {@link SimpleLock#close()} is called. 065 * <li class='note'>This class supports reentrant locking - the same thread can acquire multiple read locks 066 * or upgrade from read to write lock (if no other threads hold read locks). 067 * <li class='note'>For fair ordering, use {@link #SimpleReadWriteLock(boolean)} with <jk>true</jk>. 068 * </ul> 069 * 070 * <h5 class='section'>See Also:</h5><ul> 071 * <li class='jc'>{@link SimpleLock} - The auto-closeable lock wrapper 072 * <li class='jc'>{@link ReentrantReadWriteLock} - The underlying lock implementation 073 * </ul> 074 */ 075@SuppressWarnings({ 076 "resource" // Caller takes ownership of the returned lock via try-with-resources 077}) 078public class SimpleReadWriteLock extends ReentrantReadWriteLock { 079 private static final long serialVersionUID = 1L; 080 081 /** 082 * A no-op lock instance that performs no actual locking operations. 083 * 084 * <p> 085 * This instance can be used when locking is conditionally disabled or when you need a 086 * {@link SimpleReadWriteLock} instance but don't want any actual synchronization overhead. 087 * All lock operations on this instance are no-ops and return {@link SimpleLock#NO_OP}. 088 * 089 * <h5 class='section'>Example:</h5> 090 * <p class='bjava'> 091 * <jc>// Use NO_OP when locking is disabled</jc> 092 * <jk>var</jk> <jv>lock</jv> = isLockingEnabled() ? <jk>new</jk> SimpleReadWriteLock() : SimpleReadWriteLock.NO_OP; 093 * <jk>try</jk> (<jk>var</jk> <jv>readLock</jv> = <jv>lock</jv>.read()) { 094 * <jc>// Code works the same whether locking is enabled or not</jc> 095 * <jv>sharedResource</jv>.read(); 096 * } 097 * </p> 098 * 099 * <h5 class='section'>Notes:</h5><ul> 100 * <li class='note'>Calling {@link #read()} or {@link #write()} on this instance returns {@link SimpleLock#NO_OP}. 101 * <li class='note'>Closing the returned {@link SimpleLock} has no effect. 102 * <li class='note'>This instance is thread-safe and can be shared across threads. 103 * </ul> 104 */ 105 public static final SimpleReadWriteLock NO_OP = new SimpleReadWriteLock() { 106 private static final long serialVersionUID = 1L; 107 108 @Override 109 public SimpleLock read() { 110 return SimpleLock.NO_OP; 111 } 112 113 @Override 114 public SimpleLock write() { 115 return SimpleLock.NO_OP; 116 } 117 }; 118 119 /** 120 * Constructor. 121 * 122 * <p> 123 * Creates a new read-write lock with an unfair ordering policy. Unfair locks generally 124 * provide better throughput under high contention but may not guarantee fairness. 125 */ 126 public SimpleReadWriteLock() {} 127 128 /** 129 * Constructor. 130 * 131 * <p> 132 * Creates a new read-write lock with the specified fairness policy. 133 * 134 * <h5 class='section'>Fair vs Unfair:</h5><ul> 135 * <li class='note'><b>Fair locks</b> (<jk>true</jk>): Threads acquire locks in approximately 136 * FIFO order. Provides fairness but may have lower throughput under high contention. 137 * <li class='note'><b>Unfair locks</b> (<jk>false</jk>): No ordering guarantee. Generally provides 138 * better throughput but may starve some threads under high contention. 139 * </ul> 140 * 141 * @param fair <jk>true</jk> if this lock should use a fair ordering policy, <jk>false</jk> for unfair. 142 */ 143 public SimpleReadWriteLock(boolean fair) { 144 super(fair); 145 } 146 147 /** 148 * Acquires and returns a read lock. 149 * 150 * <p> 151 * The returned {@link SimpleLock} automatically acquires the read lock when created and releases 152 * it when {@link SimpleLock#close()} is called. Multiple threads can hold read locks simultaneously, 153 * but a write lock cannot be acquired while any read locks are held. 154 * 155 * <h5 class='section'>Example:</h5> 156 * <p class='bjava'> 157 * <jk>var</jk> <jv>lock</jv> = <jk>new</jk> SimpleReadWriteLock(); 158 * <jk>try</jk> (<jk>var</jk> <jv>readLock</jv> = <jv>lock</jv>.read()) { 159 * <jc>// Multiple threads can hold read locks simultaneously</jc> 160 * <jv>sharedResource</jv>.read(); 161 * } 162 * <jc>// Lock is automatically released</jc> 163 * </p> 164 * 165 * <h5 class='section'>Notes:</h5><ul> 166 * <li class='note'>This method blocks until the read lock can be acquired. 167 * <li class='note'>Multiple threads can hold read locks concurrently. 168 * <li class='note'>Read locks cannot be acquired while a write lock is held by another thread. 169 * <li class='note'>The same thread can acquire multiple read locks (reentrant). 170 * <li class='note'>Each call returns a new {@link SimpleLock} instance. 171 * </ul> 172 * 173 * @return A new {@link SimpleLock} that holds the read lock. The lock is automatically acquired. 174 */ 175 public SimpleLock read() { 176 return new SimpleLock(readLock()); 177 } 178 179 /** 180 * Acquires and returns a write lock. 181 * 182 * <p> 183 * The returned {@link SimpleLock} automatically acquires the write lock when created and releases 184 * it when {@link SimpleLock#close()} is called. Write locks are exclusive - only one thread can 185 * hold a write lock at a time, and no read locks can be held while a write lock is active. 186 * 187 * <h5 class='section'>Example:</h5> 188 * <p class='bjava'> 189 * <jk>var</jk> <jv>lock</jv> = <jk>new</jk> SimpleReadWriteLock(); 190 * <jk>try</jk> (<jk>var</jk> <jv>writeLock</jv> = <jv>lock</jv>.write()) { 191 * <jc>// Only one thread can hold a write lock at a time</jc> 192 * <jv>sharedResource</jv>.write(<js>"new value"</js>); 193 * } 194 * <jc>// Lock is automatically released</jc> 195 * </p> 196 * 197 * <h5 class='section'>Notes:</h5><ul> 198 * <li class='note'>This method blocks until the write lock can be acquired. 199 * <li class='note'>Write locks are exclusive - only one thread can hold a write lock at a time. 200 * <li class='note'>Write locks cannot be acquired while any read locks are held by other threads. 201 * <li class='note'>Read locks cannot be acquired while a write lock is held. 202 * <li class='note'>The same thread can acquire multiple write locks (reentrant). 203 * <li class='note'>Each call returns a new {@link SimpleLock} instance. 204 * </ul> 205 * 206 * @return A new {@link SimpleLock} that holds the write lock. The lock is automatically acquired. 207 */ 208 public SimpleLock write() { 209 return new SimpleLock(writeLock()); 210 } 211}