Source code
Description: source: package: org.apache.commons.lang3.concurrent.locks, class: LockingVisitors, class: LockVisitor

    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;
    029import org.apache.commons.lang3.function.Suppliers;
    030
    031/**
    032 * Combines the monitor and visitor pattern to work with {@link java.util.concurrent.locks.Lock locked objects}. Locked
    033 * objects are an alternative to synchronization. This, on Wikipedia, is known as the Visitor pattern
    034 * (https://en.wikipedia.org/wiki/Visitor_pattern), and from the "Gang of Four" "Design Patterns" book's Visitor pattern
    035 * [Gamma, E., Helm, R., &amp; Johnson, R. (1998). Visitor. In Design patterns elements of reusable object oriented software (pp. 331-344). Reading: Addison Wesley.].
    036 *
    037 * <p>
    038 * Locking is preferable, if there is a distinction between read access (multiple threads may have read access
    039 * concurrently), and write access (only one thread may have write access at any given time). In comparison,
    040 * synchronization doesn't support read access, because synchronized access is exclusive.
    041 * </p>
    042 * <p>
    043 * Using this class is fairly straightforward:
    044 * </p>
    045 * <ol>
    046 * <li>While still in single thread mode, create an instance of {@link LockingVisitors.StampedLockVisitor} by calling
    047 * {@link #stampedLockVisitor(Object)}, passing the object which needs to be locked. Discard all references to the
    048 * locked object. Instead, use references to the lock.</li>
    049 * <li>If you want to access the locked object, create a {@link FailableConsumer}. The consumer will receive the locked
    050 * object as a parameter. For convenience, the consumer may be implemented as a Lambda. Then invoke
    051 * {@link LockingVisitors.StampedLockVisitor#acceptReadLocked(FailableConsumer)}, or
    052 * {@link LockingVisitors.StampedLockVisitor#acceptWriteLocked(FailableConsumer)}, passing the consumer.</li>
    053 * <li>As an alternative, if you need to produce a result object, you may use a {@link FailableFunction}. This function
    054 * may also be implemented as a Lambda. To have the function executed, invoke
    055 * {@link LockingVisitors.StampedLockVisitor#applyReadLocked(FailableFunction)}, or
    056 * {@link LockingVisitors.StampedLockVisitor#applyWriteLocked(FailableFunction)}.</li>
    057 * </ol>
    058 * <p>
    059 * Example: A thread safe logger class.
    060 * </p>
    061 *
    062 * <pre>{@code
    063 *   public class SimpleLogger {
    064 *
    065 *     private final StampedLockVisitor<PrintStream> lock;
    066 *
    067 *     public SimpleLogger(OutputStream out) {
    068 *         lock = LockingVisitors.stampedLockVisitor(new PrintStream(out));
    069 *     }
    070 *
    071 *     public void log(String message) {
    072 *         lock.acceptWriteLocked(ps -> ps.println(message));
    073 *     }
    074 *
    075 *     public void log(byte[] buffer) {
    076 *         lock.acceptWriteLocked(ps -> { ps.write(buffer); ps.println(); });
    077 *     }
    078 * }
    079 * }
    080 * </pre>
    081 *
    082 * @since 3.11
    083 */
    084public class LockingVisitors {
    085
    086    /**
    087     * Wraps a domain object and a lock for access by lambdas.
    088     *
    089     * @param <O> the wrapped object type.
    090     * @param <L> the wrapped lock type.
    091     */
    092    public static class LockVisitor<O, L> {
    093
    094        /**
    095         * The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in
    096         * Java 8.
    097         */
    098        private final L lock;
    099
    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}