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.commons.lang3.concurrent.locks; 018 019import java.util.Objects; 020import java.util.concurrent.locks.Lock; 021import java.util.concurrent.locks.ReadWriteLock; 022import java.util.concurrent.locks.ReentrantReadWriteLock; 023import java.util.concurrent.locks.StampedLock; 024import java.util.function.Supplier; 025 026import org.apache.commons.lang3.function.Failable; 027import org.apache.commons.lang3.function.FailableConsumer; 028import org.apache.commons.lang3.function.FailableFunction; 029 030/** 031 * Combines the monitor and visitor pattern to work with {@link java.util.concurrent.locks.Lock locked objects}. Locked 032 * objects are an alternative to synchronization. This, on Wikipedia, is known as the Visitor pattern 033 * (https://en.wikipedia.org/wiki/Visitor_pattern), and from the "Gang of Four" "Design Patterns" book's Visitor pattern 034 * [Gamma, E., Helm, R., & Johnson, R. (1998). Visitor. In Design patterns elements of reusable object oriented software (pp. 331-344). Reading: Addison Wesley.]. 035 * 036 * <p> 037 * Locking is preferable, if there is a distinction between read access (multiple threads may have read access 038 * concurrently), and write access (only one thread may have write access at any given time). In comparison, 039 * synchronization doesn't support read access, because synchronized access is exclusive. 040 * </p> 041 * <p> 042 * Using this class is fairly straightforward: 043 * </p> 044 * <ol> 045 * <li>While still in single thread mode, create an instance of {@link LockingVisitors.StampedLockVisitor} by calling 046 * {@link #stampedLockVisitor(Object)}, passing the object which needs to be locked. Discard all references to the 047 * locked object. Instead, use references to the lock.</li> 048 * <li>If you want to access the locked object, create a {@link FailableConsumer}. The consumer will receive the locked 049 * object as a parameter. For convenience, the consumer may be implemented as a Lambda. Then invoke 050 * {@link LockingVisitors.StampedLockVisitor#acceptReadLocked(FailableConsumer)}, or 051 * {@link LockingVisitors.StampedLockVisitor#acceptWriteLocked(FailableConsumer)}, passing the consumer.</li> 052 * <li>As an alternative, if you need to produce a result object, you may use a {@link FailableFunction}. This function 053 * may also be implemented as a Lambda. To have the function executed, invoke 054 * {@link LockingVisitors.StampedLockVisitor#applyReadLocked(FailableFunction)}, or 055 * {@link LockingVisitors.StampedLockVisitor#applyWriteLocked(FailableFunction)}.</li> 056 * </ol> 057 * <p> 058 * Example: A thread safe logger class. 059 * </p> 060 * 061 * <pre> 062 * public class SimpleLogger { 063 * 064 * private final StampedLockVisitor<PrintStream> lock; 065 * 066 * public SimpleLogger(OutputStream out) { 067 * lock = LockingVisitors.stampedLockVisitor(new PrintStream(out)); 068 * } 069 * 070 * public void log(String message) { 071 * lock.acceptWriteLocked((ps) -> ps.println(message)); 072 * } 073 * 074 * public void log(byte[] buffer) { 075 * lock.acceptWriteLocked((ps) -> { ps.write(buffer); ps.println(); }); 076 * } 077 * </pre> 078 * 079 * @since 3.11 080 */ 081public class LockingVisitors { 082 083 /** 084 * Wraps a domain object and a lock for access by lambdas. 085 * 086 * @param <O> the wrapped object type. 087 * @param <L> the wrapped lock type. 088 */ 089 public static class LockVisitor<O, L> { 090 091 /** 092 * The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in 093 * Java 8. 094 */ 095 private final L lock; 096 097 /** 098 * The guarded object. 099 */ 100 private final O object; 101 102 /** 103 * Supplies the read lock, usually from the lock object. 104 */ 105 private final Supplier<Lock> readLockSupplier; 106 107 /** 108 * Supplies the write lock, usually from the lock object. 109 */ 110 private final Supplier<Lock> writeLockSupplier; 111 112 /** 113 * Constructs an instance. 114 * 115 * @param object The object to guard. 116 * @param lock The locking object. 117 * @param readLockSupplier Supplies the read lock, usually from the lock object. 118 * @param writeLockSupplier Supplies the write lock, usually from the lock object. 119 */ 120 protected LockVisitor(final O object, final L lock, final Supplier<Lock> readLockSupplier, final Supplier<Lock> writeLockSupplier) { 121 this.object = Objects.requireNonNull(object, "object"); 122 this.lock = Objects.requireNonNull(lock, "lock"); 123 this.readLockSupplier = Objects.requireNonNull(readLockSupplier, "readLockSupplier"); 124 this.writeLockSupplier = Objects.requireNonNull(writeLockSupplier, "writeLockSupplier"); 125 } 126 127 /** 128 * Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method 129 * will do (in the given order): 130 * 131 * <ol> 132 * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a 133 * lock is granted.</li> 134 * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li> 135 * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the 136 * lock will be released anyways.</li> 137 * </ol> 138 * 139 * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the 140 * consumers parameter. 141 * @see #acceptWriteLocked(FailableConsumer) 142 * @see #applyReadLocked(FailableFunction) 143 */ 144 public void acceptReadLocked(final FailableConsumer<O, ?> consumer) { 145 lockAcceptUnlock(readLockSupplier, consumer); 146 } 147 148 /** 149 * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in 150 * the given order): 151 * 152 * <ol> 153 * <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a 154 * lock is granted.</li> 155 * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li> 156 * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the 157 * lock will be released anyways.</li> 158 * </ol> 159 * 160 * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the 161 * consumers parameter. 162 * @see #acceptReadLocked(FailableConsumer) 163 * @see #applyWriteLocked(FailableFunction) 164 */ 165 public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) { 166 lockAcceptUnlock(writeLockSupplier, consumer); 167 } 168 169 /** 170 * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a 171 * result object. More precisely, what the method will do (in the given order): 172 * 173 * <ol> 174 * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a 175 * lock is granted.</li> 176 * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter, 177 * receiving the functions result.</li> 178 * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the 179 * lock will be released anyways.</li> 180 * <li>Return the result object, that has been received from the functions invocation.</li> 181 * </ol> 182 * <p> 183 * <em>Example:</em> Consider that the hidden object is a list, and we wish to know the current size of the 184 * list. This might be achieved with the following: 185 * </p> 186 * <pre> 187 * private Lock<List<Object>> listLock; 188 * 189 * public int getCurrentListSize() { 190 * final Integer sizeInteger = listLock.applyReadLocked((list) -> Integer.valueOf(list.size)); 191 * return sizeInteger.intValue(); 192 * } 193 * </pre> 194 * 195 * @param <T> The result type (both the functions, and this method's.) 196 * @param function The function, which is being invoked to compute the result. The function will receive the 197 * hidden object. 198 * @return The result object, which has been returned by the functions invocation. 199 * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend 200 * access to the hidden object beyond this methods lifetime and will therefore be prevented. 201 * @see #acceptReadLocked(FailableConsumer) 202 * @see #applyWriteLocked(FailableFunction) 203 */ 204 public <T> T applyReadLocked(final FailableFunction<O, T, ?> function) { 205 return lockApplyUnlock(readLockSupplier, function); 206 } 207 208 /** 209 * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object. 210 * More precisely, what the method will do (in the given order): 211 * 212 * <ol> 213 * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a 214 * lock is granted.</li> 215 * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter, 216 * receiving the functions result.</li> 217 * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the 218 * lock will be released anyways.</li> 219 * <li>Return the result object, that has been received from the functions invocation.</li> 220 * </ol> 221 * 222 * @param <T> The result type (both the functions, and this method's.) 223 * @param function The function, which is being invoked to compute the result. The function will receive the 224 * hidden object. 225 * @return The result object, which has been returned by the functions invocation. 226 * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend 227 * access to the hidden object beyond this methods lifetime and will therefore be prevented. 228 * @see #acceptReadLocked(FailableConsumer) 229 * @see #applyWriteLocked(FailableFunction) 230 */ 231 public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) { 232 return lockApplyUnlock(writeLockSupplier, function); 233 } 234 235 /** 236 * Gets the lock. 237 * 238 * @return the lock. 239 */ 240 public L getLock() { 241 return lock; 242 } 243 244 /** 245 * Gets the guarded object. 246 * 247 * @return the object. 248 */ 249 public O getObject() { 250 return object; 251 } 252 253 /** 254 * This method provides the default implementation for {@link #acceptReadLocked(FailableConsumer)}, and 255 * {@link #acceptWriteLocked(FailableConsumer)}. 256 * 257 * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used 258 * internally.) 259 * @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed 260 * as a parameter. 261 * @see #acceptReadLocked(FailableConsumer) 262 * @see #acceptWriteLocked(FailableConsumer) 263 */ 264 protected void lockAcceptUnlock(final Supplier<Lock> lockSupplier, final FailableConsumer<O, ?> consumer) { 265 final Lock lock = lockSupplier.get(); 266 lock.lock(); 267 try { 268 consumer.accept(object); 269 } catch (final Throwable t) { 270 throw Failable.rethrow(t); 271 } finally { 272 lock.unlock(); 273 } 274 } 275 276 /** 277 * This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and 278 * {@link #applyWriteLocked(FailableFunction)}. 279 * 280 * @param <T> The result type (both the functions, and this method's.) 281 * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used 282 * internally.) 283 * @param function The function, which is being invoked to compute the result object. This function will receive 284 * the locked (hidden) object as a parameter. 285 * @return The result object, which has been returned by the functions invocation. 286 * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend 287 * access to the hidden object beyond this methods lifetime and will therefore be prevented. 288 * @see #applyReadLocked(FailableFunction) 289 * @see #applyWriteLocked(FailableFunction) 290 */ 291 protected <T> T lockApplyUnlock(final Supplier<Lock> lockSupplier, final FailableFunction<O, T, ?> function) { 292 final Lock lock = lockSupplier.get(); 293 lock.lock(); 294 try { 295 return function.apply(object); 296 } catch (final Throwable t) { 297 throw Failable.rethrow(t); 298 } finally { 299 lock.unlock(); 300 } 301 } 302 303 } 304 305 /** 306 * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic 307 * idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the 308 * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)}, 309 * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the 310 * necessary protections are guaranteed. 311 * 312 * @param <O> The locked (hidden) objects type. 313 */ 314 public static class ReadWriteLockVisitor<O> extends LockVisitor<O, ReadWriteLock> { 315 316 /** 317 * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing 318 * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead. 319 * 320 * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object. 321 * @param readWriteLock the lock to use. 322 */ 323 protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) { 324 super(object, readWriteLock, readWriteLock::readLock, readWriteLock::writeLock); 325 } 326 } 327 328 /** 329 * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic 330 * idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the 331 * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)}, 332 * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the 333 * necessary protections are guaranteed. 334 * 335 * @param <O> The locked (hidden) objects type. 336 */ 337 public static class StampedLockVisitor<O> extends LockVisitor<O, StampedLock> { 338 339 /** 340 * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing 341 * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead. 342 * 343 * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object. 344 * @param stampedLock the lock to use. 345 */ 346 protected StampedLockVisitor(final O object, final StampedLock stampedLock) { 347 super(object, stampedLock, stampedLock::asReadLock, stampedLock::asWriteLock); 348 } 349 } 350 351 /** 352 * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object and lock. 353 * 354 * @param <O> The locked objects type. 355 * @param object The locked (hidden) object. 356 * @param readWriteLock The lock to use. 357 * @return The created instance, a {@link StampedLockVisitor lock} for the given object. 358 * @since 3.13.0 359 */ 360 public static <O> ReadWriteLockVisitor<O> create(final O object, final ReadWriteLock readWriteLock) { 361 return new LockingVisitors.ReadWriteLockVisitor<>(object, readWriteLock); 362 } 363 364 /** 365 * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object. 366 * 367 * @param <O> The locked objects type. 368 * @param object The locked (hidden) object. 369 * @return The created instance, a {@link StampedLockVisitor lock} for the given object. 370 */ 371 public static <O> ReadWriteLockVisitor<O> reentrantReadWriteLockVisitor(final O object) { 372 return create(object, new ReentrantReadWriteLock()); 373 } 374 375 /** 376 * Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object. 377 * 378 * @param <O> The locked objects type. 379 * @param object The locked (hidden) object. 380 * @return The created instance, a {@link StampedLockVisitor lock} for the given object. 381 */ 382 public static <O> StampedLockVisitor<O> stampedLockVisitor(final O object) { 383 return new LockingVisitors.StampedLockVisitor<>(object, new StampedLock()); 384 } 385 386}