LockingVisitors.java

  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. import java.util.Objects;
  19. import java.util.concurrent.locks.Lock;
  20. import java.util.concurrent.locks.ReadWriteLock;
  21. import java.util.concurrent.locks.ReentrantReadWriteLock;
  22. import java.util.concurrent.locks.StampedLock;
  23. import java.util.function.Supplier;

  24. import org.apache.commons.lang3.function.Failable;
  25. import org.apache.commons.lang3.function.FailableConsumer;
  26. import org.apache.commons.lang3.function.FailableFunction;
  27. import org.apache.commons.lang3.function.Suppliers;

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

  82.     /**
  83.      * Wraps a domain object and a lock for access by lambdas.
  84.      *
  85.      * @param <O> the wrapped object type.
  86.      * @param <L> the wrapped lock type.
  87.      */
  88.     public static class LockVisitor<O, L> {

  89.         /**
  90.          * The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in
  91.          * Java 8.
  92.          */
  93.         private final L lock;

  94.         /**
  95.          * The guarded object.
  96.          */
  97.         private final O object;

  98.         /**
  99.          * Supplies the read lock, usually from the lock object.
  100.          */
  101.         private final Supplier<Lock> readLockSupplier;

  102.         /**
  103.          * Supplies the write lock, usually from the lock object.
  104.          */
  105.         private final Supplier<Lock> writeLockSupplier;

  106.         /**
  107.          * Constructs an instance.
  108.          *
  109.          * @param object The object to guard.
  110.          * @param lock The locking object.
  111.          * @param readLockSupplier Supplies the read lock, usually from the lock object.
  112.          * @param writeLockSupplier Supplies the write lock, usually from the lock object.
  113.          */
  114.         protected LockVisitor(final O object, final L lock, final Supplier<Lock> readLockSupplier, final Supplier<Lock> writeLockSupplier) {
  115.             this.object = Objects.requireNonNull(object, "object");
  116.             this.lock = Objects.requireNonNull(lock, "lock");
  117.             this.readLockSupplier = Objects.requireNonNull(readLockSupplier, "readLockSupplier");
  118.             this.writeLockSupplier = Objects.requireNonNull(writeLockSupplier, "writeLockSupplier");
  119.         }

  120.         /**
  121.          * Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method
  122.          * will do (in the given order):
  123.          *
  124.          * <ol>
  125.          * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
  126.          * lock is granted.</li>
  127.          * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
  128.          * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
  129.          * lock will be released anyways.</li>
  130.          * </ol>
  131.          *
  132.          * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
  133.          *        consumers parameter.
  134.          * @see #acceptWriteLocked(FailableConsumer)
  135.          * @see #applyReadLocked(FailableFunction)
  136.          */
  137.         public void acceptReadLocked(final FailableConsumer<O, ?> consumer) {
  138.             lockAcceptUnlock(readLockSupplier, consumer);
  139.         }

  140.         /**
  141.          * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in
  142.          * the given order):
  143.          *
  144.          * <ol>
  145.          * <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a
  146.          * lock is granted.</li>
  147.          * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
  148.          * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
  149.          * lock will be released anyways.</li>
  150.          * </ol>
  151.          *
  152.          * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
  153.          *        consumers parameter.
  154.          * @see #acceptReadLocked(FailableConsumer)
  155.          * @see #applyWriteLocked(FailableFunction)
  156.          */
  157.         public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) {
  158.             lockAcceptUnlock(writeLockSupplier, consumer);
  159.         }

  160.         /**
  161.          * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a
  162.          * result object. More precisely, what the method will do (in the given order):
  163.          *
  164.          * <ol>
  165.          * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
  166.          * lock is granted.</li>
  167.          * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
  168.          * receiving the functions result.</li>
  169.          * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
  170.          * lock will be released anyways.</li>
  171.          * <li>Return the result object, that has been received from the functions invocation.</li>
  172.          * </ol>
  173.          * <p>
  174.          * <em>Example:</em> Consider that the hidden object is a list, and we wish to know the current size of the
  175.          * list. This might be achieved with the following:
  176.          * </p>
  177.          * <pre>{@code
  178.          * private Lock<List<Object>> listLock;
  179.          *
  180.          * public int getCurrentListSize() {
  181.          *     final Integer sizeInteger = listLock.applyReadLocked(list -> Integer.valueOf(list.size));
  182.          *     return sizeInteger.intValue();
  183.          * }
  184.          * }
  185.          * </pre>
  186.          *
  187.          * @param <T> The result type (both the functions, and this method's.)
  188.          * @param function The function, which is being invoked to compute the result. The function will receive the
  189.          *        hidden object.
  190.          * @return The result object, which has been returned by the functions invocation.
  191.          * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
  192.          *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
  193.          * @see #acceptReadLocked(FailableConsumer)
  194.          * @see #applyWriteLocked(FailableFunction)
  195.          */
  196.         public <T> T applyReadLocked(final FailableFunction<O, T, ?> function) {
  197.             return lockApplyUnlock(readLockSupplier, function);
  198.         }

  199.         /**
  200.          * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object.
  201.          * More precisely, what the method will do (in the given order):
  202.          *
  203.          * <ol>
  204.          * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
  205.          * lock is granted.</li>
  206.          * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
  207.          * receiving the functions result.</li>
  208.          * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
  209.          * lock will be released anyways.</li>
  210.          * <li>Return the result object, that has been received from the functions invocation.</li>
  211.          * </ol>
  212.          *
  213.          * @param <T> The result type (both the functions, and this method's.)
  214.          * @param function The function, which is being invoked to compute the result. The function will receive the
  215.          *        hidden object.
  216.          * @return The result object, which has been returned by the functions invocation.
  217.          * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
  218.          *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
  219.          * @see #acceptReadLocked(FailableConsumer)
  220.          * @see #applyWriteLocked(FailableFunction)
  221.          */
  222.         public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) {
  223.             return lockApplyUnlock(writeLockSupplier, function);
  224.         }

  225.         /**
  226.          * Gets the lock.
  227.          *
  228.          * @return the lock.
  229.          */
  230.         public L getLock() {
  231.             return lock;
  232.         }

  233.         /**
  234.          * Gets the guarded object.
  235.          *
  236.          * @return the object.
  237.          */
  238.         public O getObject() {
  239.             return object;
  240.         }

  241.         /**
  242.          * This method provides the default implementation for {@link #acceptReadLocked(FailableConsumer)}, and
  243.          * {@link #acceptWriteLocked(FailableConsumer)}.
  244.          *
  245.          * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
  246.          *        internally.)
  247.          * @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed
  248.          *        as a parameter.
  249.          * @see #acceptReadLocked(FailableConsumer)
  250.          * @see #acceptWriteLocked(FailableConsumer)
  251.          */
  252.         protected void lockAcceptUnlock(final Supplier<Lock> lockSupplier, final FailableConsumer<O, ?> consumer) {
  253.             final Lock lock = Objects.requireNonNull(Suppliers.get(lockSupplier), "lock");
  254.             lock.lock();
  255.             try {
  256.                 if (consumer != null) {
  257.                     consumer.accept(object);
  258.                 }
  259.             } catch (final Throwable t) {
  260.                 throw Failable.rethrow(t);
  261.             } finally {
  262.                 lock.unlock();
  263.             }
  264.         }

  265.         /**
  266.          * This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and
  267.          * {@link #applyWriteLocked(FailableFunction)}.
  268.          *
  269.          * @param <T> The result type (both the functions, and this method's.)
  270.          * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
  271.          *        internally.)
  272.          * @param function The function, which is being invoked to compute the result object. This function will receive
  273.          *        the locked (hidden) object as a parameter.
  274.          * @return The result object, which has been returned by the functions invocation.
  275.          * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
  276.          *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
  277.          * @see #applyReadLocked(FailableFunction)
  278.          * @see #applyWriteLocked(FailableFunction)
  279.          */
  280.         protected <T> T lockApplyUnlock(final Supplier<Lock> lockSupplier, final FailableFunction<O, T, ?> function) {
  281.             final Lock lock = Objects.requireNonNull(Suppliers.get(lockSupplier), "lock");
  282.             lock.lock();
  283.             try {
  284.                 return function.apply(object);
  285.             } catch (final Throwable t) {
  286.                 throw Failable.rethrow(t);
  287.             } finally {
  288.                 lock.unlock();
  289.             }
  290.         }

  291.     }

  292.     /**
  293.      * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
  294.      * idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the
  295.      * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
  296.      * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
  297.      * necessary protections are guaranteed.
  298.      *
  299.      * @param <O> The locked (hidden) objects type.
  300.      */
  301.     public static class ReadWriteLockVisitor<O> extends LockVisitor<O, ReadWriteLock> {

  302.         /**
  303.          * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
  304.          * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
  305.          *
  306.          * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
  307.          * @param readWriteLock the lock to use.
  308.          */
  309.         protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) {
  310.             super(object, readWriteLock, readWriteLock::readLock, readWriteLock::writeLock);
  311.         }
  312.     }

  313.     /**
  314.      * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
  315.      * idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the
  316.      * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
  317.      * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
  318.      * necessary protections are guaranteed.
  319.      *
  320.      * @param <O> The locked (hidden) objects type.
  321.      */
  322.     public static class StampedLockVisitor<O> extends LockVisitor<O, StampedLock> {

  323.         /**
  324.          * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
  325.          * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
  326.          *
  327.          * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
  328.          * @param stampedLock the lock to use.
  329.          */
  330.         protected StampedLockVisitor(final O object, final StampedLock stampedLock) {
  331.             super(object, stampedLock, stampedLock::asReadLock, stampedLock::asWriteLock);
  332.         }
  333.     }

  334.     /**
  335.      * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object and lock.
  336.      *
  337.      * @param <O> The locked objects type.
  338.      * @param object The locked (hidden) object.
  339.      * @param readWriteLock The lock to use.
  340.      * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
  341.      * @since 3.13.0
  342.      */
  343.     public static <O> ReadWriteLockVisitor<O> create(final O object, final ReadWriteLock readWriteLock) {
  344.         return new LockingVisitors.ReadWriteLockVisitor<>(object, readWriteLock);
  345.     }

  346.     /**
  347.      * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object.
  348.      *
  349.      * @param <O> The locked objects type.
  350.      * @param object The locked (hidden) object.
  351.      * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
  352.      */
  353.     public static <O> ReadWriteLockVisitor<O> reentrantReadWriteLockVisitor(final O object) {
  354.         return create(object, new ReentrantReadWriteLock());
  355.     }

  356.     /**
  357.      * Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object.
  358.      *
  359.      * @param <O> The locked objects type.
  360.      * @param object The locked (hidden) object.
  361.      * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
  362.      */
  363.     public static <O> StampedLockVisitor<O> stampedLockVisitor(final O object) {
  364.         return new LockingVisitors.StampedLockVisitor<>(object, new StampedLock());
  365.     }

  366.     /**
  367.      * Make private in 4.0.
  368.      *
  369.      * @deprecated TODO Make private in 4.0.
  370.      */
  371.     @Deprecated
  372.     public LockingVisitors() {
  373.         // empty
  374.     }
  375. }