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  
30  /**
31   * Combines the monitor and visitor pattern to work with {@link java.util.concurrent.locks.Lock locked objects}. Locked
32   * objects are an alternative to synchronization. This, on Wikipedia, is known as the Visitor pattern
33   * (https://en.wikipedia.org/wiki/Visitor_pattern), and from the "Gang of Four" "Design Patterns" book's Visitor pattern
34   * [Gamma, E., Helm, R., & Johnson, R. (1998). Visitor. In Design patterns elements of reusable object oriented software (pp. 331-344). Reading: Addison Wesley.].
35   *
36   * <p>
37   * Locking is preferable, if there is a distinction between read access (multiple threads may have read access
38   * concurrently), and write access (only one thread may have write access at any given time). In comparison,
39   * synchronization doesn't support read access, because synchronized access is exclusive.
40   * </p>
41   * <p>
42   * Using this class is fairly straightforward:
43   * </p>
44   * <ol>
45   * <li>While still in single thread mode, create an instance of {@link LockingVisitors.StampedLockVisitor} by calling
46   * {@link #stampedLockVisitor(Object)}, passing the object which needs to be locked. Discard all references to the
47   * locked object. Instead, use references to the lock.</li>
48   * <li>If you want to access the locked object, create a {@link FailableConsumer}. The consumer will receive the locked
49   * object as a parameter. For convenience, the consumer may be implemented as a Lambda. Then invoke
50   * {@link LockingVisitors.StampedLockVisitor#acceptReadLocked(FailableConsumer)}, or
51   * {@link LockingVisitors.StampedLockVisitor#acceptWriteLocked(FailableConsumer)}, passing the consumer.</li>
52   * <li>As an alternative, if you need to produce a result object, you may use a {@link FailableFunction}. This function
53   * may also be implemented as a Lambda. To have the function executed, invoke
54   * {@link LockingVisitors.StampedLockVisitor#applyReadLocked(FailableFunction)}, or
55   * {@link LockingVisitors.StampedLockVisitor#applyWriteLocked(FailableFunction)}.</li>
56   * </ol>
57   * <p>
58   * Example: A thread safe logger class.
59   * </p>
60   *
61   * <pre>
62   *   public class SimpleLogger {
63   *
64   *     private final StampedLockVisitor&lt;PrintStream&gt; lock;
65   *
66   *     public SimpleLogger(OutputStream out) {
67   *         lock = LockingVisitors.stampedLockVisitor(new PrintStream(out));
68   *     }
69   *
70   *     public void log(String message) {
71   *         lock.acceptWriteLocked((ps) -&gt; ps.println(message));
72   *     }
73   *
74   *     public void log(byte[] buffer) {
75   *         lock.acceptWriteLocked((ps) -&gt; { ps.write(buffer); ps.println(); });
76   *     }
77   * </pre>
78   *
79   * @since 3.11
80   */
81  public class LockingVisitors {
82  
83      /**
84       * Wraps a domain object and a lock for access by lambdas.
85       *
86       * @param <O> the wrapped object type.
87       * @param <L> the wrapped lock type.
88       */
89      public static class LockVisitor<O, L> {
90  
91          /**
92           * The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in
93           * Java 8.
94           */
95          private final L lock;
96  
97          /**
98           * The guarded object.
99           */
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&lt;List&lt;Object&gt;&gt; listLock;
188          *
189          * public int getCurrentListSize() {
190          *     final Integer sizeInteger = listLock.applyReadLocked((list) -&gt; 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 }