View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.concurrent.locks;
18  
19  import java.util.Objects;
20  import java.util.concurrent.locks.Lock;
21  import java.util.concurrent.locks.ReadWriteLock;
22  import java.util.concurrent.locks.ReentrantReadWriteLock;
23  import java.util.concurrent.locks.StampedLock;
24  import java.util.function.Supplier;
25  
26  import org.apache.commons.lang3.function.Failable;
27  import org.apache.commons.lang3.function.FailableConsumer;
28  import org.apache.commons.lang3.function.FailableFunction;
29  import org.apache.commons.lang3.function.Suppliers;
30  
31  /**
32   * Combines the monitor and visitor pattern to work with {@link java.util.concurrent.locks.Lock locked objects}. Locked
33   * objects are an alternative to synchronization. This, on Wikipedia, is known as the Visitor pattern
34   * (https://en.wikipedia.org/wiki/Visitor_pattern), and from the "Gang of Four" "Design Patterns" book's Visitor pattern
35   * [Gamma, E., Helm, R., & Johnson, R. (1998). Visitor. In Design patterns elements of reusable object oriented software (pp. 331-344). Reading: Addison Wesley.].
36   *
37   * <p>
38   * Locking is preferable, if there is a distinction between read access (multiple threads may have read access
39   * concurrently), and write access (only one thread may have write access at any given time). In comparison,
40   * synchronization doesn't support read access, because synchronized access is exclusive.
41   * </p>
42   * <p>
43   * Using this class is fairly straightforward:
44   * </p>
45   * <ol>
46   * <li>While still in single thread mode, create an instance of {@link LockingVisitors.StampedLockVisitor} by calling
47   * {@link #stampedLockVisitor(Object)}, passing the object which needs to be locked. Discard all references to the
48   * locked object. Instead, use references to the lock.</li>
49   * <li>If you want to access the locked object, create a {@link FailableConsumer}. The consumer will receive the locked
50   * object as a parameter. For convenience, the consumer may be implemented as a Lambda. Then invoke
51   * {@link LockingVisitors.StampedLockVisitor#acceptReadLocked(FailableConsumer)}, or
52   * {@link LockingVisitors.StampedLockVisitor#acceptWriteLocked(FailableConsumer)}, passing the consumer.</li>
53   * <li>As an alternative, if you need to produce a result object, you may use a {@link FailableFunction}. This function
54   * may also be implemented as a Lambda. To have the function executed, invoke
55   * {@link LockingVisitors.StampedLockVisitor#applyReadLocked(FailableFunction)}, or
56   * {@link LockingVisitors.StampedLockVisitor#applyWriteLocked(FailableFunction)}.</li>
57   * </ol>
58   * <p>
59   * Example: A thread safe logger class.
60   * </p>
61   *
62   * <pre>{@code
63   *   public class SimpleLogger {
64   *
65   *     private final StampedLockVisitor<PrintStream> lock;
66   *
67   *     public SimpleLogger(OutputStream out) {
68   *         lock = LockingVisitors.stampedLockVisitor(new PrintStream(out));
69   *     }
70   *
71   *     public void log(String message) {
72   *         lock.acceptWriteLocked(ps -> ps.println(message));
73   *     }
74   *
75   *     public void log(byte[] buffer) {
76   *         lock.acceptWriteLocked(ps -> { ps.write(buffer); ps.println(); });
77   *     }
78   * }
79   * }
80   * </pre>
81   *
82   * @since 3.11
83   */
84  public class LockingVisitors {
85  
86      /**
87       * Wraps a domain object and a lock for access by lambdas.
88       *
89       * @param <O> the wrapped object type.
90       * @param <L> the wrapped lock type.
91       */
92      public static class LockVisitor<O, L> {
93  
94          /**
95           * The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in
96           * Java 8.
97           */
98          private final L lock;
99  
100         /**
101          * The guarded object.
102          */
103         private final O object;
104 
105         /**
106          * Supplies the read lock, usually from the lock object.
107          */
108         private final Supplier<Lock> readLockSupplier;
109 
110         /**
111          * Supplies the write lock, usually from the lock object.
112          */
113         private final Supplier<Lock> writeLockSupplier;
114 
115         /**
116          * Constructs an instance.
117          *
118          * @param object The object to guard.
119          * @param lock The locking object.
120          * @param readLockSupplier Supplies the read lock, usually from the lock object.
121          * @param writeLockSupplier Supplies the write lock, usually from the lock object.
122          */
123         protected LockVisitor(final O object, final L lock, final Supplier<Lock> readLockSupplier, final Supplier<Lock> writeLockSupplier) {
124             this.object = Objects.requireNonNull(object, "object");
125             this.lock = Objects.requireNonNull(lock, "lock");
126             this.readLockSupplier = Objects.requireNonNull(readLockSupplier, "readLockSupplier");
127             this.writeLockSupplier = Objects.requireNonNull(writeLockSupplier, "writeLockSupplier");
128         }
129 
130         /**
131          * Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method
132          * will do (in the given order):
133          *
134          * <ol>
135          * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
136          * lock is granted.</li>
137          * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
138          * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
139          * lock will be released anyways.</li>
140          * </ol>
141          *
142          * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
143          *        consumers parameter.
144          * @see #acceptWriteLocked(FailableConsumer)
145          * @see #applyReadLocked(FailableFunction)
146          */
147         public void acceptReadLocked(final FailableConsumer<O, ?> consumer) {
148             lockAcceptUnlock(readLockSupplier, consumer);
149         }
150 
151         /**
152          * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in
153          * the given order):
154          *
155          * <ol>
156          * <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a
157          * lock is granted.</li>
158          * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
159          * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
160          * lock will be released anyways.</li>
161          * </ol>
162          *
163          * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
164          *        consumers parameter.
165          * @see #acceptReadLocked(FailableConsumer)
166          * @see #applyWriteLocked(FailableFunction)
167          */
168         public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) {
169             lockAcceptUnlock(writeLockSupplier, consumer);
170         }
171 
172         /**
173          * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a
174          * result object. More precisely, what the method will do (in the given order):
175          *
176          * <ol>
177          * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
178          * lock is granted.</li>
179          * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
180          * receiving the functions result.</li>
181          * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
182          * lock will be released anyways.</li>
183          * <li>Return the result object, that has been received from the functions invocation.</li>
184          * </ol>
185          * <p>
186          * <em>Example:</em> Consider that the hidden object is a list, and we wish to know the current size of the
187          * list. This might be achieved with the following:
188          * </p>
189          * <pre>{@code
190          * private Lock<List<Object>> listLock;
191          *
192          * public int getCurrentListSize() {
193          *     final Integer sizeInteger = listLock.applyReadLocked(list -> Integer.valueOf(list.size));
194          *     return sizeInteger.intValue();
195          * }
196          * }
197          * </pre>
198          *
199          * @param <T> The result type (both the functions, and this method's.)
200          * @param function The function, which is being invoked to compute the result. The function will receive the
201          *        hidden object.
202          * @return The result object, which has been returned by the functions invocation.
203          * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
204          *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
205          * @see #acceptReadLocked(FailableConsumer)
206          * @see #applyWriteLocked(FailableFunction)
207          */
208         public <T> T applyReadLocked(final FailableFunction<O, T, ?> function) {
209             return lockApplyUnlock(readLockSupplier, function);
210         }
211 
212         /**
213          * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object.
214          * More precisely, what the method will do (in the given order):
215          *
216          * <ol>
217          * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
218          * lock is granted.</li>
219          * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
220          * receiving the functions result.</li>
221          * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
222          * lock will be released anyways.</li>
223          * <li>Return the result object, that has been received from the functions invocation.</li>
224          * </ol>
225          *
226          * @param <T> The result type (both the functions, and this method's.)
227          * @param function The function, which is being invoked to compute the result. The function will receive the
228          *        hidden object.
229          * @return The result object, which has been returned by the functions invocation.
230          * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
231          *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
232          * @see #acceptReadLocked(FailableConsumer)
233          * @see #applyWriteLocked(FailableFunction)
234          */
235         public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) {
236             return lockApplyUnlock(writeLockSupplier, function);
237         }
238 
239         /**
240          * Gets the lock.
241          *
242          * @return the lock.
243          */
244         public L getLock() {
245             return lock;
246         }
247 
248         /**
249          * Gets the guarded object.
250          *
251          * @return the object.
252          */
253         public O getObject() {
254             return object;
255         }
256 
257         /**
258          * This method provides the default implementation for {@link #acceptReadLocked(FailableConsumer)}, and
259          * {@link #acceptWriteLocked(FailableConsumer)}.
260          *
261          * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
262          *        internally.)
263          * @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed
264          *        as a parameter.
265          * @see #acceptReadLocked(FailableConsumer)
266          * @see #acceptWriteLocked(FailableConsumer)
267          */
268         protected void lockAcceptUnlock(final Supplier<Lock> lockSupplier, final FailableConsumer<O, ?> consumer) {
269             final Lock lock = Objects.requireNonNull(Suppliers.get(lockSupplier), "lock");
270             lock.lock();
271             try {
272                 if (consumer != null) {
273                     consumer.accept(object);
274                 }
275             } catch (final Throwable t) {
276                 throw Failable.rethrow(t);
277             } finally {
278                 lock.unlock();
279             }
280         }
281 
282         /**
283          * This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and
284          * {@link #applyWriteLocked(FailableFunction)}.
285          *
286          * @param <T> The result type (both the functions, and this method's.)
287          * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
288          *        internally.)
289          * @param function The function, which is being invoked to compute the result object. This function will receive
290          *        the locked (hidden) object as a parameter.
291          * @return The result object, which has been returned by the functions invocation.
292          * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
293          *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
294          * @see #applyReadLocked(FailableFunction)
295          * @see #applyWriteLocked(FailableFunction)
296          */
297         protected <T> T lockApplyUnlock(final Supplier<Lock> lockSupplier, final FailableFunction<O, T, ?> function) {
298             final Lock lock = Objects.requireNonNull(Suppliers.get(lockSupplier), "lock");
299             lock.lock();
300             try {
301                 return function.apply(object);
302             } catch (final Throwable t) {
303                 throw Failable.rethrow(t);
304             } finally {
305                 lock.unlock();
306             }
307         }
308 
309     }
310 
311     /**
312      * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
313      * idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the
314      * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
315      * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
316      * necessary protections are guaranteed.
317      *
318      * @param <O> The locked (hidden) objects type.
319      */
320     public static class ReadWriteLockVisitor<O> extends LockVisitor<O, ReadWriteLock> {
321 
322         /**
323          * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
324          * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
325          *
326          * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
327          * @param readWriteLock the lock to use.
328          */
329         protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) {
330             super(object, readWriteLock, readWriteLock::readLock, readWriteLock::writeLock);
331         }
332     }
333 
334     /**
335      * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
336      * idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the
337      * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
338      * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
339      * necessary protections are guaranteed.
340      *
341      * @param <O> The locked (hidden) objects type.
342      */
343     public static class StampedLockVisitor<O> extends LockVisitor<O, StampedLock> {
344 
345         /**
346          * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
347          * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
348          *
349          * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
350          * @param stampedLock the lock to use.
351          */
352         protected StampedLockVisitor(final O object, final StampedLock stampedLock) {
353             super(object, stampedLock, stampedLock::asReadLock, stampedLock::asWriteLock);
354         }
355     }
356 
357     /**
358      * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object and lock.
359      *
360      * @param <O> The locked objects type.
361      * @param object The locked (hidden) object.
362      * @param readWriteLock The lock to use.
363      * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
364      * @since 3.13.0
365      */
366     public static <O> ReadWriteLockVisitor<O> create(final O object, final ReadWriteLock readWriteLock) {
367         return new LockingVisitors.ReadWriteLockVisitor<>(object, readWriteLock);
368     }
369 
370     /**
371      * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object.
372      *
373      * @param <O> The locked objects type.
374      * @param object The locked (hidden) object.
375      * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
376      */
377     public static <O> ReadWriteLockVisitor<O> reentrantReadWriteLockVisitor(final O object) {
378         return create(object, new ReentrantReadWriteLock());
379     }
380 
381     /**
382      * Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object.
383      *
384      * @param <O> The locked objects type.
385      * @param object The locked (hidden) object.
386      * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
387      */
388     public static <O> StampedLockVisitor<O> stampedLockVisitor(final O object) {
389         return new LockingVisitors.StampedLockVisitor<>(object, new StampedLock());
390     }
391 
392     /**
393      * Make private in 4.0.
394      *
395      * @deprecated TODO Make private in 4.0.
396      */
397     @Deprecated
398     public LockingVisitors() {
399         // empty
400     }
401 }