From ed0f5bea111c449fa17478d7e0c7c37c3ede3863 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 18 Jun 2020 12:48:46 -0400 Subject: [PATCH] Reimplement such that locking and unlocking take place in the same method. --- .../commons/lang3/concurrent/Locks.java | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/concurrent/Locks.java b/src/main/java/org/apache/commons/lang3/concurrent/Locks.java index f78a9dd6d..f8ecae965 100644 --- a/src/main/java/org/apache/commons/lang3/concurrent/Locks.java +++ b/src/main/java/org/apache/commons/lang3/concurrent/Locks.java @@ -18,36 +18,36 @@ package org.apache.commons.lang3.concurrent; import java.util.Objects; import java.util.concurrent.locks.StampedLock; +import java.util.function.LongSupplier; import org.apache.commons.lang3.function.Failable; import org.apache.commons.lang3.function.FailableConsumer; import org.apache.commons.lang3.function.FailableFunction; - /** * Utility class for working with {@link java.util.concurrent.locks.Lock locked objects}. Locked objects are an * alternative to synchronization. * - * Locking is preferable, if there is a distinction between read access (multiple threads may have read - * access concurrently), and write access (only one thread may have write access at any given time. - * In comparison, synchronization doesn't support read access, because synchronized access is exclusive. + * Locking is preferable, if there is a distinction between read access (multiple threads may have read access + * concurrently), and write access (only one thread may have write access at any given time. In comparison, + * synchronization doesn't support read access, because synchronized access is exclusive. * * Using this class is fairly straightforward: *
    - *
  1. While still in single thread mode, create an instance of {@link Locks.Lock} by calling - * {@link #lock(Object)}, passing the object, which needs to be locked. Discard all - * references to the locked object. Instead, use references to the lock.
  2. - *
  3. If you want to access the locked object, create a {@link FailableConsumer}. The consumer - * will receive the locked object as a parameter. For convenience, the consumer may be - * implemented as a Lambda. Then invoke {@link Locks.Lock#runReadLocked(FailableConsumer)}, - * or {@link Locks.Lock#runWriteLocked(FailableConsumer)}, passing the consumer.
  4. - *
  5. As an alternative, if you need to produce a result object, you may use a - * {@link FailableFunction}. This function may also be implemented as a Lambda. To - * have the function executed, invoke {@link Locks.Lock#callReadLocked(FailableFunction)}, or - * {@link Locks.Lock#callWriteLocked(FailableFunction)}.
  6. + *
  7. While still in single thread mode, create an instance of {@link Locks.Lock} by calling {@link #lock(Object)}, + * passing the object, which needs to be locked. Discard all references to the locked object. Instead, use references to + * the lock.
  8. + *
  9. If you want to access the locked object, create a {@link FailableConsumer}. The consumer will receive the locked + * object as a parameter. For convenience, the consumer may be implemented as a Lambda. Then invoke + * {@link Locks.Lock#runReadLocked(FailableConsumer)}, or {@link Locks.Lock#runWriteLocked(FailableConsumer)}, passing + * the consumer.
  10. + *
  11. As an alternative, if you need to produce a result object, you may use a {@link FailableFunction}. This function + * may also be implemented as a Lambda. To have the function executed, invoke + * {@link Locks.Lock#callReadLocked(FailableFunction)}, or {@link Locks.Lock#callWriteLocked(FailableFunction)}.
  12. *
* * Example: A thread safe logger class. + * *
  *   public class SimpleLogger {
  *     private final Lock<PrintStream> lock;
@@ -65,6 +65,7 @@ import org.apache.commons.lang3.function.FailableFunction;
  *         lock.runWriteLocked((ps) -> { ps.write(buffer); ps.println(); });
  *     }
  * 
+ * * @since 3.11 */ public class Locks { @@ -73,40 +74,42 @@ public class Locks { private final O lockedObject; private final StampedLock lock = new StampedLock(); - public Lock(O lockedObject) { + public Lock(final O lockedObject) { this.lockedObject = Objects.requireNonNull(lockedObject, "Locked Object"); } - public void runReadLocked(FailableConsumer consumer) { - acceptLocked(lock.readLock(), consumer); + public void runReadLocked(final FailableConsumer consumer) { + acceptLocked(() -> lock.readLock(), consumer); } - public void runWriteLocked(FailableConsumer consumer) { - acceptLocked(lock.writeLock(), consumer); + public void runWriteLocked(final FailableConsumer consumer) { + acceptLocked(() -> lock.writeLock(), consumer); } - public T callReadLocked(FailableFunction function) { - return applyLocked(lock.readLock(), function); + public T callReadLocked(final FailableFunction function) { + return applyLocked(() -> lock.readLock(), function); } - public T callWriteLocked(FailableFunction function) { - return applyLocked(lock.writeLock(), function); + public T callWriteLocked(final FailableFunction function) { + return applyLocked(() -> lock.writeLock(), function); } - protected void acceptLocked(long stamp, FailableConsumer consumer) { + protected void acceptLocked(final LongSupplier stampSupplier, final FailableConsumer consumer) { + final long stamp = stampSupplier.getAsLong(); try { consumer.accept(lockedObject); - } catch (Throwable t) { + } catch (final Throwable t) { throw Failable.rethrow(t); } finally { lock.unlock(stamp); } } - protected T applyLocked(long stamp, FailableFunction function) { + protected T applyLocked(final LongSupplier stampSupplier, final FailableFunction function) { + final long stamp = stampSupplier.getAsLong(); try { return function.apply(lockedObject); - } catch (Throwable t) { + } catch (final Throwable t) { throw Failable.rethrow(t); } finally { lock.unlock(stamp); @@ -114,7 +117,7 @@ public class Locks { } } - public static Locks.Lock lock(O object) { + public static Locks.Lock lock(final O object) { return new Locks.Lock<>(object); } }