Skip to content

Commit ae28edf

Browse files
committed
Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.reentrantLockVisitor(Object)
- Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.create(Object, ReentrantLock) - Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.ReentrantLockVisitor
1 parent 6b07957 commit ae28edf

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

src/changes/changes.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ The <action> type attribute can be add,update,fix,remove.
132132
<action type="add" dev="ggregory" due-to="Pankraz76, Gary Gregory">Add ObjectUtils.getIfNull(Object, Object) and deprecate defaultIfNull(Object, Object).</action>
133133
<action type="add" dev="ggregory" due-to="Gary Gregory">org.apache.commons.lang3.mutable.Mutable now extends Supplier.</action>
134134
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.CharUtils.isHex(char).</action>
135-
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.CharUtils.isOctal(char).</action>
135+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.CharUtils.isOctal(char).</action>
136+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.reentrantLockVisitor(Object).</action>
137+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.create(Object, ReentrantLock).</action>
138+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.ReentrantLockVisitor.</action>
136139
<!-- UPDATE -->
137140
<action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 73 to 85 #1267, #1277, #1283, #1288, #1302, #1377.</action>
138141
<action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">[site] Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #1300.</action>

src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Objects;
2020
import java.util.concurrent.locks.Lock;
2121
import java.util.concurrent.locks.ReadWriteLock;
22+
import java.util.concurrent.locks.ReentrantLock;
2223
import java.util.concurrent.locks.ReentrantReadWriteLock;
2324
import java.util.concurrent.locks.StampedLock;
2425
import java.util.function.Supplier;
@@ -325,6 +326,30 @@ protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock
325326
}
326327
}
327328

329+
/**
330+
* This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
331+
* idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the
332+
* accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
333+
* {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
334+
* necessary protections are guaranteed.
335+
*
336+
* @param <O> The locked (hidden) objects type.
337+
* @since 3.18.0
338+
*/
339+
public static class ReentrantLockVisitor<O> extends LockVisitor<O, ReentrantLock> {
340+
341+
/**
342+
* Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
343+
* only. In general, it is suggested to use {@link LockingVisitors#reentrantLockVisitor(Object)} instead.
344+
*
345+
* @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
346+
* @param reentrantLock the lock to use.
347+
*/
348+
protected ReentrantLockVisitor(final O object, final ReentrantLock reentrantLock) {
349+
super(object, reentrantLock, () -> reentrantLock, () -> reentrantLock);
350+
}
351+
}
352+
328353
/**
329354
* This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
330355
* idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the
@@ -361,6 +386,31 @@ public static <O> ReadWriteLockVisitor<O> create(final O object, final ReadWrite
361386
return new LockingVisitors.ReadWriteLockVisitor<>(object, readWriteLock);
362387
}
363388

389+
/**
390+
* Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object and lock.
391+
*
392+
* @param <O> The locked objects type.
393+
* @param object The locked (hidden) object.
394+
* @param reentrantLock The lock to use.
395+
* @return The created instance, a {@link StampedLockVisitor lock} for the given object.
396+
* @since 3.18.0
397+
*/
398+
public static <O> ReentrantLockVisitor<O> create(final O object, final ReentrantLock reentrantLock) {
399+
return new LockingVisitors.ReentrantLockVisitor<>(object, reentrantLock);
400+
}
401+
402+
/**
403+
* Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object.
404+
*
405+
* @param <O> The locked objects type.
406+
* @param object The locked (hidden) object.
407+
* @return The created instance, a {@link StampedLockVisitor lock} for the given object.
408+
* @since 3.18.0
409+
*/
410+
public static <O> ReentrantLockVisitor<O> reentrantLockVisitor(final O object) {
411+
return create(object, new ReentrantLock());
412+
}
413+
364414
/**
365415
* Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object.
366416
*

src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.time.Duration;
2525
import java.util.concurrent.atomic.AtomicInteger;
2626
import java.util.concurrent.locks.ReadWriteLock;
27+
import java.util.concurrent.locks.ReentrantLock;
2728
import java.util.concurrent.locks.ReentrantReadWriteLock;
2829
import java.util.function.LongConsumer;
2930

@@ -34,6 +35,8 @@
3435
import org.apache.commons.lang3.concurrent.locks.LockingVisitors.StampedLockVisitor;
3536
import org.apache.commons.lang3.function.FailableConsumer;
3637
import org.junit.jupiter.api.Test;
38+
import org.junit.jupiter.params.ParameterizedTest;
39+
import org.junit.jupiter.params.provider.ValueSource;
3740

3841
/**
3942
* Tests {@link LockingVisitors}.
@@ -56,7 +59,7 @@ private void runTest(final Duration delay, final boolean exclusiveLock, final Lo
5659
assertNotNull(visitor.getLock());
5760
assertNotNull(visitor.getObject());
5861
final boolean[] runningValues = new boolean[10];
59-
final long startTimeMillis = System.currentTimeMillis();
62+
// final long startTimeMillis = System.currentTimeMillis();
6063
for (int i = 0; i < booleanValues.length; i++) {
6164
final int index = i;
6265
final FailableConsumer<boolean[], ?> consumer = b -> {
@@ -78,7 +81,7 @@ private void runTest(final Duration delay, final boolean exclusiveLock, final Lo
7881
while (containsTrue(runningValues)) {
7982
ThreadUtils.sleep(SHORT_DELAY);
8083
}
81-
final long endTimeMillis = System.currentTimeMillis();
84+
// final long endTimeMillis = System.currentTimeMillis();
8285
for (final boolean booleanValue : booleanValues) {
8386
assertTrue(booleanValue);
8487
}
@@ -126,6 +129,22 @@ void testReentrantReadWriteLockNotExclusive() throws Exception {
126129
LockingVisitors.reentrantReadWriteLockVisitor(booleanValues));
127130
}
128131

132+
@Test
133+
void testReentrantLock() throws Exception {
134+
// If our threads are running concurrently, then we expect to be faster than running one after the other.
135+
final boolean[] booleanValues = new boolean[10];
136+
runTest(DELAY, false, millis -> assertTrue(millis < TOTAL_DELAY.toMillis()), booleanValues, LockingVisitors.reentrantLockVisitor(booleanValues));
137+
}
138+
139+
@ParameterizedTest
140+
@ValueSource(booleans = { true, false })
141+
void testReentrantLockFairness(final boolean fairness) throws Exception {
142+
// If our threads are running concurrently, then we expect to be faster than running one after the other.
143+
final boolean[] booleanValues = new boolean[10];
144+
runTest(DELAY, false, millis -> assertTrue(millis < TOTAL_DELAY.toMillis()), booleanValues,
145+
LockingVisitors.create(booleanValues, new ReentrantLock(fairness)));
146+
}
147+
129148
@Test
130149
void testResultValidation() {
131150
final Object hidden = new Object();

0 commit comments

Comments
 (0)