Merge branch 'master' into release

This commit is contained in:
Gary Gregory 2020-07-12 09:00:30 -04:00
commit 01514294a5
3 changed files with 168 additions and 364 deletions

View File

@ -45,7 +45,7 @@ The <action> type attribute can be add,update,fix,remove.
</properties>
<body>
<release version="3.11" date="2020-07-10" description="New features and bug fixes.">
<release version="3.11" date="2020-07-12" description="New features and bug fixes.">
<action type="update" dev="chtompki" due-to="Jin Xu">Refine test output for FastDateParserTest</action>
<action issue="LANG-1549" type="update" dev="chtompki" due-to="Jin Xu">CharSequenceUtils.lastIndexOf : remake it</action>
<action type="update" dev="kinow" due-to="XenoAmess">remove encoding and docEncoding and use inherited values from commons-parent</action>

View File

@ -21,7 +21,6 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
import org.apache.commons.lang3.function.Failable;
@ -29,17 +28,23 @@ import org.apache.commons.lang3.function.FailableConsumer;
import org.apache.commons.lang3.function.FailableFunction;
/**
* <p>
* Combines the monitor and visitor pattern to work with {@link java.util.concurrent.locks.Lock locked objects}. Locked
* objects are an alternative to synchronization.
*
* objects are an alternative to synchronization. This, on Wikipedia, is known as the Visitor pattern
* (https://en.wikipedia.org/wiki/Visitor_pattern), and from the "Gang of Four" "Design Patterns" book's Visitor pattern
* [Gamma, E., Helm, R., &amp; Johnson, R. (1998). Visitor. In Design patterns elements of reusable object oriented software (pp. 331-344). Reading: Addison Wesley.].
* </p>
* <p>
* 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,
* 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.
*
* </p>
* <p>
* Using this class is fairly straightforward:
* </p>
* <ol>
* <li>While still in single thread mode, create an instance of {@link LockingVisitors.StampedLockVisitor} by calling
* {@link #stampedLockVisitor(Object)}, passing the object, which needs to be locked. Discard all references to the
* {@link #stampedLockVisitor(Object)}, passing the object which needs to be locked. Discard all references to the
* locked object. Instead, use references to the lock.</li>
* <li>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
@ -50,8 +55,9 @@ import org.apache.commons.lang3.function.FailableFunction;
* {@link LockingVisitors.StampedLockVisitor#applyReadLocked(FailableFunction)}, or
* {@link LockingVisitors.StampedLockVisitor#applyWriteLocked(FailableFunction)}.</li>
* </ol>
*
* <p>
* Example: A thread safe logger class.
* </p>
*
* <pre>
* public class SimpleLogger {
@ -76,21 +82,55 @@ import org.apache.commons.lang3.function.FailableFunction;
public class LockingVisitors {
/**
* Wraps a domain object for access by lambdas.
* Wraps a domain object and a lock for access by lambdas.
*
* @param <O> the wrapped object type.
* @param <L> the wrapped lock type.
*/
public abstract static class AbstractLockVisitor<O extends Object> {
protected AbstractLockVisitor(final O object) {
super();
this.object = Objects.requireNonNull(object, "object");
}
protected final O object;
public static class LockVisitor<O, L> {
/**
* The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in
* Java 8.
*/
private final L lock;
/**
* The guarded object.
*/
private final O object;
/**
* Supplies the read lock, usually from the lock object.
*/
private final Supplier<Lock> readLockSupplier;
/**
* Supplies the write lock, usually from the lock object.
*/
private final Supplier<Lock> writeLockSupplier;
/**
* Constructs an instance.
*
* @param object The object to guard.
* @param lock The locking object.
* @param readLockSupplier Supplies the read lock, usually from the lock object.
* @param writeLockSupplier Supplies the write lock, usually from the lock object.
*/
protected LockVisitor(final O object, L lock, Supplier<Lock> readLockSupplier, Supplier<Lock> writeLockSupplier) {
super();
this.object = Objects.requireNonNull(object, "object");
this.lock = Objects.requireNonNull(lock, "lock");
this.readLockSupplier = Objects.requireNonNull(readLockSupplier, "readLockSupplier");
this.writeLockSupplier = Objects.requireNonNull(writeLockSupplier, "writeLockSupplier");
}
/**
* <p>
* Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method
* will do (in the given order):
* </p>
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
@ -104,11 +144,15 @@ public class LockingVisitors {
* @see #acceptWriteLocked(FailableConsumer)
* @see #applyReadLocked(FailableFunction)
*/
public abstract void acceptReadLocked(FailableConsumer<O, ?> consumer);
public void acceptReadLocked(FailableConsumer<O, ?> consumer) {
lockAcceptUnlock(readLockSupplier, consumer);
}
/**
* <p>
* Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in
* the given order):
* </p>
* <ol>
* <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
@ -122,11 +166,15 @@ public class LockingVisitors {
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
public abstract void acceptWriteLocked(FailableConsumer<O, ?> consumer);
public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) {
lockAcceptUnlock(writeLockSupplier, consumer);
}
/**
* <p>
* Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a
* result object. More precisely, what the method will do (in the given order):
* </p>
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
@ -136,10 +184,10 @@ public class LockingVisitors {
* lock will be released anyways.</li>
* <li>Return the result object, that has been received from the functions invocation.</li>
* </ol>
*
* <em>Example:</em> Suggest, that the hidden object is a list, and we wish to know the current size of the
* <p>
* <em>Example:</em> Consider that the hidden object is a list, and we wish to know the current size of the
* list. This might be achieved with the following:
*
* </p>
* <pre>
* private Lock&lt;List&lt;Object&gt;&gt; listLock;
*
@ -158,11 +206,15 @@ public class LockingVisitors {
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
public abstract <T> T applyReadLocked(FailableFunction<O, T, ?> function);
public <T> T applyReadLocked(FailableFunction<O, T, ?> function) {
return lockApplyUnlock(readLockSupplier, function);
}
/**
* <p>
* Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object.
* More precisely, what the method will do (in the given order):
* </p>
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
@ -182,7 +234,77 @@ public class LockingVisitors {
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
public abstract <T> T applyWriteLocked(FailableFunction<O, T, ?> function);
public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) {
return lockApplyUnlock(writeLockSupplier, function);
}
/**
* Gets the lock.
*
* @return the lock.
*/
public L getLock() {
return lock;
}
/**
* Gets the guarded object.
*
* @return the object.
*/
public O getObject() {
return object;
}
/**
* This method provides the default implementation for {@link #acceptReadLocked(FailableConsumer)}, and
* {@link #acceptWriteLocked(FailableConsumer)}.
*
* @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
* internally.)
* @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed
* as a parameter.
* @see #acceptReadLocked(FailableConsumer)
* @see #acceptWriteLocked(FailableConsumer)
*/
protected void lockAcceptUnlock(final Supplier<Lock> lockSupplier, final FailableConsumer<O, ?> consumer) {
final Lock lock = lockSupplier.get();
lock.lock();
try {
consumer.accept(object);
} catch (Throwable t) {
throw Failable.rethrow(t);
} finally {
lock.unlock();
}
}
/**
* This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and
* {@link #applyWriteLocked(FailableFunction)}.
*
* @param <T> The result type (both the functions, and this method's.)
* @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
* internally.)
* @param function The function, which is being invoked to compute the result object. This function will receive
* the locked (hidden) object as a parameter.
* @return The result object, which has been returned by the functions invocation.
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
* @see #applyReadLocked(FailableFunction)
* @see #applyWriteLocked(FailableFunction)
*/
protected <T> T lockApplyUnlock(final Supplier<Lock> lockSupplier, final FailableFunction<O, T, ?> function) {
final Lock lock = lockSupplier.get();
lock.lock();
try {
return function.apply(object);
} catch (Throwable t) {
throw Failable.rethrow(t);
} finally {
lock.unlock();
}
}
}
@ -195,8 +317,7 @@ public class LockingVisitors {
*
* @param <O> The locked (hidden) objects type.
*/
public static class ReadWriteLockVisitor<O extends Object> extends AbstractLockVisitor<O> {
private final ReadWriteLock readWriteLock;
public static class ReadWriteLockVisitor<O> extends LockVisitor<O, ReadWriteLock> {
/**
* Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
@ -205,360 +326,32 @@ public class LockingVisitors {
* @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
* @param readWriteLock the lock to use.
*/
public ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) {
super(object);
this.readWriteLock = readWriteLock;
}
/**
* Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method
* will do (in the given order):
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
* <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
* lock will be released anyways.</li>
* </ol>
*
* @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
* consumers parameter.
* @see #acceptWriteLocked(FailableConsumer)
* @see #applyReadLocked(FailableFunction)
*/
@Override
public void acceptReadLocked(final FailableConsumer<O, ?> consumer) {
lockAcceptUnlock(() -> readWriteLock.readLock(), consumer);
}
/**
* Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in
* the given order):
* <ol>
* <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
* <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
* lock will be released anyways.</li>
* </ol>
*
* @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
* consumers parameter.
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
@Override
public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) {
lockAcceptUnlock(() -> readWriteLock.writeLock(), consumer);
}
/**
* Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a
* result object. More precisely, what the method will do (in the given order):
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
* <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
* receiving the functions result.</li>
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
* lock will be released anyways.</li>
* <li>Return the result object, that has been received from the functions invocation.</li>
* </ol>
*
* <em>Example:</em> Suggest, that the hidden object is a list, and we wish to know the current size of the
* list. This might be achieved with the following:
*
* <pre>
* private Lock&lt;List&lt;Object&gt;&gt; listLock;
*
* public int getCurrentListSize() {
* final Integer sizeInteger = listLock.applyReadLocked((list) -&gt; Integer.valueOf(list.size));
* return sizeInteger.intValue();
* }
* </pre>
*
* @param <T> The result type (both the functions, and this method's.)
* @param function The function, which is being invoked to compute the result. The function will receive the
* hidden object.
* @return The result object, which has been returned by the functions invocation.
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
@Override
public <T> T applyReadLocked(final FailableFunction<O, T, ?> function) {
return lockApplyUnlock(() -> readWriteLock.readLock(), function);
}
/**
* Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object.
* More precisely, what the method will do (in the given order):
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
* <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
* receiving the functions result.</li>
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
* lock will be released anyways.</li>
* <li>Return the result object, that has been received from the functions invocation.</li>
* </ol>
*
* @param <T> The result type (both the functions, and this method's.)
* @param function The function, which is being invoked to compute the result. The function will receive the
* hidden object.
* @return The result object, which has been returned by the functions invocation.
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
@Override
public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) {
return lockApplyUnlock(() -> readWriteLock.writeLock(), function);
}
/**
* This method provides the actual implementation for {@link #acceptReadLocked(FailableConsumer)}, and
* {@link #acceptWriteLocked(FailableConsumer)}.
*
* @param lock A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
* internally.)
* @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed
* as a parameter.
* @see #acceptReadLocked(FailableConsumer)
* @see #acceptWriteLocked(FailableConsumer)
* @see #lockApplyUnlock(LongSupplier, FailableFunction)
*/
private void lockAcceptUnlock(final Supplier<Lock> lockSupplier, final FailableConsumer<O, ?> consumer) {
final Lock lock = lockSupplier.get();
try {
consumer.accept(object);
} catch (Throwable t) {
throw Failable.rethrow(t);
} finally {
lock.unlock();
}
}
/**
* This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and
* {@link #applyWriteLocked(FailableFunction)}.
*
* @param <T> The result type (both the functions, and this method's.)
* @param lock A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
* internally.)
* @param function The function, which is being invoked to compute the result object. This function will receive
* the locked (hidden) object as a parameter.
* @return The result object, which has been returned by the functions invocation.
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
* @see #applyReadLocked(FailableFunction)
* @see #applyWriteLocked(FailableFunction)
* @see #lockAcceptUnlock(LongSupplier, FailableConsumer)
*/
private <T> T lockApplyUnlock(final Supplier<Lock> lockSupplier, final FailableFunction<O, T, ?> function) {
final Lock lock = lockSupplier.get();
try {
return function.apply(object);
} catch (Throwable t) {
throw Failable.rethrow(t);
} finally {
lock.unlock();
}
protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) {
super(object, readWriteLock, readWriteLock::readLock, readWriteLock::writeLock);
}
}
/**
* This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
* idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the
* idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the
* accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
* {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
* necessary protections are guaranteed.
*
* @param <O> The locked (hidden) objects type.
*/
public static class StampedLockVisitor<O extends Object> extends AbstractLockVisitor<O> {
private final StampedLock stampedLock = new StampedLock();
public static class StampedLockVisitor<O> extends LockVisitor<O, StampedLock> {
/**
* Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
* only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
*
* @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
* @param stampedLock the lock to use.
*/
public StampedLockVisitor(final O object) {
super(object);
protected StampedLockVisitor(final O object, StampedLock stampedLock) {
super(object, stampedLock, stampedLock::asReadLock, stampedLock::asWriteLock);
}
/**
* Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method
* will do (in the given order):
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
* <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
* lock will be released anyways.</li>
* </ol>
*
* @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
* consumers parameter.
* @see #acceptWriteLocked(FailableConsumer)
* @see #applyReadLocked(FailableFunction)
*/
@Override
public void acceptReadLocked(final FailableConsumer<O, ?> consumer) {
lockAcceptUnlock(() -> stampedLock.readLock(), consumer);
}
/**
* Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in
* the given order):
* <ol>
* <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
* <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
* lock will be released anyways.</li>
* </ol>
*
* @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
* consumers parameter.
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
@Override
public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) {
lockAcceptUnlock(() -> stampedLock.writeLock(), consumer);
}
/**
* Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a
* result object. More precisely, what the method will do (in the given order):
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
* <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
* receiving the functions result.</li>
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
* lock will be released anyways.</li>
* <li>Return the result object, that has been received from the functions invocation.</li>
* </ol>
*
* <em>Example:</em> Suggest, that the hidden object is a list, and we wish to know the current size of the
* list. This might be achieved with the following:
*
* <pre>
* private Lock&lt;List&lt;Object&gt;&gt; listLock;
*
* public int getCurrentListSize() {
* final Integer sizeInteger = listLock.applyReadLocked((list) -&gt; Integer.valueOf(list.size));
* return sizeInteger.intValue();
* }
* </pre>
*
* @param <T> The result type (both the functions, and this method's.)
* @param function The function, which is being invoked to compute the result. The function will receive the
* hidden object.
* @return The result object, which has been returned by the functions invocation.
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
@Override
public <T> T applyReadLocked(final FailableFunction<O, T, ?> function) {
return lockApplyUnlock(() -> stampedLock.readLock(), function);
}
/**
* Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object.
* More precisely, what the method will do (in the given order):
* <ol>
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
* lock is granted.</li>
* <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
* receiving the functions result.</li>
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
* lock will be released anyways.</li>
* <li>Return the result object, that has been received from the functions invocation.</li>
* </ol>
*
* @param <T> The result type (both the functions, and this method's.)
* @param function The function, which is being invoked to compute the result. The function will receive the
* hidden object.
* @return The result object, which has been returned by the functions invocation.
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
* @see #acceptReadLocked(FailableConsumer)
* @see #applyWriteLocked(FailableFunction)
*/
@Override
public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) {
return lockApplyUnlock(() -> stampedLock.writeLock(), function);
}
/**
* This method provides the actual implementation for {@link #acceptReadLocked(FailableConsumer)}, and
* {@link #acceptWriteLocked(FailableConsumer)}.
*
* @param stampSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock}
* is used internally.)
* @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed
* as a parameter.
* @see #acceptReadLocked(FailableConsumer)
* @see #acceptWriteLocked(FailableConsumer)
* @see #lockApplyUnlock(LongSupplier, FailableFunction)
*/
private void lockAcceptUnlock(final LongSupplier stampSupplier, final FailableConsumer<O, ?> consumer) {
final long stamp = stampSupplier.getAsLong();
try {
consumer.accept(object);
} catch (Throwable t) {
throw Failable.rethrow(t);
} finally {
stampedLock.unlock(stamp);
}
}
/**
* This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and
* {@link #applyWriteLocked(FailableFunction)}.
*
* @param <T> The result type (both the functions, and this method's.)
* @param stampSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock}
* is used internally.)
* @param function The function, which is being invoked to compute the result object. This function will receive
* the locked (hidden) object as a parameter.
* @return The result object, which has been returned by the functions invocation.
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
* @see #applyReadLocked(FailableFunction)
* @see #applyWriteLocked(FailableFunction)
* @see #lockAcceptUnlock(LongSupplier, FailableConsumer)
*/
private <T> T lockApplyUnlock(final LongSupplier stampSupplier, final FailableFunction<O, T, ?> function) {
final long stamp = stampSupplier.getAsLong();
try {
return function.apply(object);
} catch (Throwable t) {
throw Failable.rethrow(t);
} finally {
stampedLock.unlock(stamp);
}
}
}
/**
* Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object.
*
* @param <O> The locked objects type.
* @param object The locked (hidden) object.
* @return The created instance, a {@link StampedLockVisitor lock} for the given object.
*/
public static <O> StampedLockVisitor<O> stampedLockVisitor(final O object) {
return new LockingVisitors.StampedLockVisitor<>(object);
}
/**
@ -572,4 +365,15 @@ public class LockingVisitors {
return new LockingVisitors.ReadWriteLockVisitor<>(object, new ReentrantReadWriteLock());
}
/**
* Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object.
*
* @param <O> The locked objects type.
* @param object The locked (hidden) object.
* @return The created instance, a {@link StampedLockVisitor lock} for the given object.
*/
public static <O> StampedLockVisitor<O> stampedLockVisitor(final O object) {
return new LockingVisitors.StampedLockVisitor<>(object, new StampedLock());
}
}

View File

@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.function.LongConsumer;
import org.apache.commons.lang3.concurrent.locks.LockingVisitors.AbstractLockVisitor;
import org.apache.commons.lang3.concurrent.locks.LockingVisitors.LockVisitor;
import org.apache.commons.lang3.concurrent.locks.LockingVisitors.StampedLockVisitor;
import org.apache.commons.lang3.function.FailableConsumer;
import org.junit.jupiter.api.Test;
@ -80,18 +80,18 @@ public class LockingVisitorsTest {
public void testResultValidation() {
final Object hidden = new Object();
final StampedLockVisitor<Object> lock = LockingVisitors.stampedLockVisitor(hidden);
final Object o1 = lock.applyReadLocked((h) -> {
final Object o1 = lock.applyReadLocked(h -> {
return new Object(); });
assertNotNull(o1);
assertNotSame(hidden, o1);
final Object o2 = lock.applyWriteLocked((h) -> {
final Object o2 = lock.applyWriteLocked(h -> {
return new Object(); });
assertNotNull(o2);
assertNotSame(hidden, o2);
}
private void runTest(final long delayMillis, final boolean exclusiveLock, final LongConsumer runTimeCheck,
boolean[] booleanValues, AbstractLockVisitor<boolean[]> visitor) throws InterruptedException {
boolean[] booleanValues, LockVisitor<boolean[], ?> visitor) throws InterruptedException {
final boolean[] runningValues = new boolean[10];
final long startTime = System.currentTimeMillis();