Adding the Locks class.
This commit is contained in:
parent
71ee021ca5
commit
c141bc961c
|
@ -46,7 +46,7 @@ The <action> type attribute can be add,update,fix,remove.
|
|||
<body>
|
||||
|
||||
<release version="3.11" date="2020-MM-DD" description="New features and bug fixes..">
|
||||
<action type="fix" dev="kinow" due-to="contextshuffling">Fix Javdoc for StringUtils.appendIfMissingIgnoreCase() #507.</action>
|
||||
<action type="fix" dev="kinow" due-to="contextshuffling">Fix Javadoc for StringUtils.appendIfMissingIgnoreCase() #507.</action>
|
||||
<action type="update" dev="ggregory">org.junit-pioneer:junit-pioneer 0.5.4 -> 0.6.0.</action>
|
||||
<action type="update" dev="ggregory">org.junit.jupiter:junit-jupiter 5.6.0 -> 5.6.1.</action>
|
||||
<action type="update" dev="ggregory">com.github.spotbugs:spotbugs 4.0.0 -> 4.0.3.</action>
|
||||
|
@ -59,6 +59,7 @@ The <action> type attribute can be add,update,fix,remove.
|
|||
<action type="update" dev="ggregory" due-to="Arend v. Reinersdorff, Bruno P. Kinoshita">(Javadoc) Fix return tag for throwableOf*() methods #518.</action>
|
||||
<action type="add" dev="ggregory" due-to="XenoAmess, Gary Gregory">Add ArrayUtils.isSameLength() to compare more array types #430.</action>
|
||||
<action issue="LANG-1545" type="update" dev="ggregory" due-to="XenoAmess, Gary Gregory">CharSequenceUtils.regionMatches is wrong dealing with Georgian.</action>
|
||||
<action type="add" dev="jochen">Added the Locks class as a convenient possibility to deal with locked objects.</action>
|
||||
</release>
|
||||
|
||||
<release version="3.10" date="2020-03-22" description="New features and bug fixes. Requires Java 8, supports Java 9, 10, 11.">
|
||||
|
@ -275,7 +276,7 @@ The <action> type attribute can be add,update,fix,remove.
|
|||
<action issue="LANG-1195" type="add" dev="pschumacher" due-to="Derek C. Ashmore">Enhance MethodUtils to allow invocation of private methods</action>
|
||||
<action issue="LANG-1199" type="fix" dev="pschumacher" due-to="M. Steiger">Fix implementation of StringUtils.getJaroWinklerDistance()</action>
|
||||
<action issue="LANG-1244" type="fix" dev="pschumacher" due-to="jjbankert">Fix dead links in StringUtils.getLevenshteinDistance() javadoc</action>
|
||||
<action issue="LANG-1242" type="fix" dev="pschumacher" due-to="Neal Stewart">"\u2284":"⊄" mapping missing from EntityArrays#HTML40_EXTENDED_ESCAPE</action>
|
||||
<action issue="LANG-1242" type="fix" dev="pschumacher" due-to="Neal Stewart">"\u2284":"nsub" mapping missing from EntityArrays#HTML40_EXTENDED_ESCAPE</action>
|
||||
<action issue="LANG-1243" type="update" dev="sebb">Simplify ArrayUtils removeElements by using new decrementAndGet() method</action>
|
||||
<action issue="LANG-1189" type="add" dev="sebb" due-to="haiyang li / Matthew Bartenschlag ">Add getAndIncrement/getAndDecrement/getAndAdd/incrementAndGet/decrementAndGet/addAndGet in Mutable* classes</action>
|
||||
<action issue="LANG-1240" type="update" dev="pschumacher" due-to="zhanhb">Optimize BitField constructor implementation</action>
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
|
||||
import org.apache.commons.lang3.Functions.FailableConsumer;
|
||||
import org.apache.commons.lang3.Functions.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.
|
||||
*
|
||||
* Using this class is fairly straightforward:
|
||||
* <ol>
|
||||
* <li>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.</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 Lamba. Then invoke {@link Locks.Lock#runReadLocked(FailableConsumer)},
|
||||
* or {@link Locks.Lock#runWriteLocked(FailableConsumer)}, passing the consumer.</li>
|
||||
* <li>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)}.</li>
|
||||
* </ol>
|
||||
*
|
||||
* Example: A thread safe logger class.
|
||||
* <pre>
|
||||
* public class SimpleLogger {
|
||||
* private final Lock<PrintStream> lock;
|
||||
*
|
||||
* public SimpleLogger(OutputStream out) {
|
||||
* PrintStream ps = new Printstream(out);
|
||||
* lock = Locks.lock(ps);
|
||||
* }
|
||||
*
|
||||
* public void log(String message) {
|
||||
* lock.runWriteLocked((ps) -> ps.println(message));
|
||||
* }
|
||||
*
|
||||
* public void log(byte[] buffer) {
|
||||
* lock.runWriteLocked((ps) -> { ps.write(buffer); ps.println(); });
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class Locks {
|
||||
public static class Lock<O extends Object> {
|
||||
private final O lockedObject;
|
||||
private final StampedLock lock = new StampedLock();
|
||||
|
||||
public Lock(O lockedObject) {
|
||||
this.lockedObject = Objects.requireNonNull(lockedObject, "Locked Object");
|
||||
}
|
||||
|
||||
public void runReadLocked(FailableConsumer<O,?> consumer) {
|
||||
runLocked(lock.readLock(), consumer);
|
||||
}
|
||||
|
||||
public void runWriteLocked(FailableConsumer<O,?> consumer) {
|
||||
runLocked(lock.writeLock(), consumer);
|
||||
}
|
||||
|
||||
public <T> T callReadLocked(FailableFunction<O,T,?> function) {
|
||||
return callLocked(lock.readLock(), function);
|
||||
}
|
||||
|
||||
public <T> T callWriteLocked(FailableFunction<O,T,?> function) {
|
||||
return callLocked(lock.writeLock(), function);
|
||||
}
|
||||
|
||||
protected void runLocked(long stamp, FailableConsumer<O,?> consumer) {
|
||||
try {
|
||||
consumer.accept(lockedObject);
|
||||
} catch (Throwable t) {
|
||||
throw Functions.rethrow(t);
|
||||
} finally {
|
||||
lock.unlock(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T callLocked(long stamp, FailableFunction<O,T,?> function) {
|
||||
try {
|
||||
return function.apply(lockedObject);
|
||||
} catch (Throwable t) {
|
||||
throw Functions.rethrow(t);
|
||||
} finally {
|
||||
lock.unlock(stamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <O extends Object> Locks.Lock<O> lock(O object) {
|
||||
return new Locks.Lock<O>(object);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.apache.commons.lang3.Functions.FailableConsumer;
|
||||
import org.apache.commons.lang3.Locks.Lock;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class LocksTest {
|
||||
@Test
|
||||
void testReadLock() throws Exception {
|
||||
final long DELAY=3000;
|
||||
final boolean[] booleanValues = new boolean[10];
|
||||
final Lock<boolean[]> lock = Locks.lock(booleanValues);
|
||||
final boolean[] runningValues = new boolean[10];
|
||||
|
||||
final long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < booleanValues.length; i++) {
|
||||
final int index = i;
|
||||
final FailableConsumer<boolean[],?> consumer = (b) -> {
|
||||
b[index] = false;
|
||||
Thread.sleep(DELAY);
|
||||
b[index] = true;
|
||||
modify(runningValues, index, false);
|
||||
};
|
||||
final Thread t = new Thread(() -> lock.runReadLocked(consumer));
|
||||
modify(runningValues, i, true);
|
||||
t.start();
|
||||
}
|
||||
while (someValueIsTrue(runningValues)) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
final long endTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < booleanValues.length; i++) {
|
||||
assertTrue(booleanValues[i]);
|
||||
}
|
||||
// If our threads would be running in exclusive mode, then we'd need
|
||||
// at least DELAY milliseconds for each.
|
||||
assertTrue((endTime-startTime) < booleanValues.length*DELAY);
|
||||
}
|
||||
|
||||
protected void modify(boolean[] booleanArray, int offset, boolean value) {
|
||||
synchronized(booleanArray) {
|
||||
booleanArray[offset] = value;
|
||||
}
|
||||
}
|
||||
protected boolean someValueIsTrue(boolean[] booleanArray) {
|
||||
synchronized(booleanArray) {
|
||||
for (int i = 0; i < booleanArray.length; i++) {
|
||||
if (booleanArray[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue