You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/03/21 13:19:18 UTC
[commons-lang] branch master updated: Refactor to create the mapping function once.
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 85751a1 Refactor to create the mapping function once.
85751a1 is described below
commit 85751a118e38f23b9009ccab1cdd3ea3b1d2dc5d
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Mar 21 09:19:13 2022 -0400
Refactor to create the mapping function once.
Add FutureTasks.
---
src/changes/changes.xml | 1 +
.../commons/lang3/concurrent/FutureTasks.java | 45 ++++++++++++
.../apache/commons/lang3/concurrent/Memoizer.java | 82 ++++++++--------------
.../commons/lang3/concurrent/FutureTasksTest.java | 40 +++++++++++
4 files changed, 117 insertions(+), 51 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9720770..e0197e3 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -122,6 +122,7 @@ The <action> type attribute can be add,update,fix,remove.
<action type="add" dev="ggregory" due-to="Gary Gregory">Update ArchUtils.getProcessor(String) for "aarch64".</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add JavaVersion.JAVA_18.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add TimeZones.toTimeZone(TimeZone).</action>
+ <action type="add" dev="ggregory" due-to="Gary Gregory">Add FutureTasks.</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump spotbugs-maven-plugin from 4.2.0 to 4.5.0.0 #735, #808, #822, #834.</action>
<action type="update" dev="ggregory" due-to="Dependabot, XenoAmess">Bump actions/cache from v2.1.4 to v2.1.7 #742, #752, #764, #833.</action>
diff --git a/src/main/java/org/apache/commons/lang3/concurrent/FutureTasks.java b/src/main/java/org/apache/commons/lang3/concurrent/FutureTasks.java
new file mode 100644
index 0000000..ce325c8
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/concurrent/FutureTasks.java
@@ -0,0 +1,45 @@
+/*
+ * 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.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Consists of utility methods that work with {@link FutureTask}.
+ *
+ * @since 3.13.0
+ */
+public class FutureTasks {
+
+ private FutureTasks() {
+ // No instances needed.
+ }
+
+ /**
+ * Creates a {@code FutureTask} and runs the given {@code Callable}.
+ *
+ * @param <V> The result type returned by this FutureTask's {@code get} methods.
+ * @param callable the Callable task.
+ * @return a new FutureTask.
+ */
+ public static <V> FutureTask<V> run(final Callable<V> callable) {
+ final FutureTask<V> futureTask = new FutureTask<>(callable);
+ futureTask.run();
+ return futureTask;
+ }
+}
diff --git a/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java b/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
index ab3feea..d53318d 100644
--- a/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
+++ b/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
@@ -21,39 +21,33 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
+import java.util.function.Function;
/**
* <p>
- * Definition of an interface for a wrapper around a calculation that takes a
- * single parameter and returns a result. The results for the calculation will
- * be cached for future requests.
+ * Definition of an interface for a wrapper around a calculation that takes a single parameter and returns a result. The
+ * results for the calculation will be cached for future requests.
* </p>
* <p>
- * This is not a fully functional cache, there is no way of limiting or removing
- * results once they have been generated. However, it is possible to get the
- * implementation to regenerate the result for a given parameter, if an error
- * was thrown during the previous calculation, by setting the option during the
- * construction of the class. If this is not set the class will return the
- * cached exception.
+ * This is not a fully functional cache, there is no way of limiting or removing results once they have been generated.
+ * However, it is possible to get the implementation to regenerate the result for a given parameter, if an error was
+ * thrown during the previous calculation, by setting the option during the construction of the class. If this is not
+ * set the class will return the cached exception.
* </p>
* <p>
- * Thanks should go to Brian Goetz, Tim Peierls and the members of JCP JSR-166
- * Expert Group for coming up with the original implementation of the class. It
- * was also published within Java Concurrency in Practice as a sample.
+ * Thanks should go to Brian Goetz, Tim Peierls and the members of JCP JSR-166 Expert Group for coming up with the
+ * original implementation of the class. It was also published within Java Concurrency in Practice as a sample.
* </p>
*
- * @param <I>
- * the type of the input to the calculation
- * @param <O>
- * the type of the output of the calculation
+ * @param <I> the type of the input to the calculation
+ * @param <O> the type of the output of the calculation
*
* @since 3.6
*/
public class Memoizer<I, O> implements Computable<I, O> {
private final ConcurrentMap<I, Future<O>> cache = new ConcurrentHashMap<>();
- private final Computable<I, O> computable;
+ private Function<? super I, ? extends Future<O>> mappingFunction;
private final boolean recalculate;
/**
@@ -61,13 +55,11 @@ public class Memoizer<I, O> implements Computable<I, O> {
* Constructs a Memoizer for the provided Computable calculation.
* </p>
* <p>
- * If a calculation is thrown an exception for any reason, this exception
- * will be cached and returned for all future calls with the provided
- * parameter.
+ * If a calculation is thrown an exception for any reason, this exception will be cached and returned for all future
+ * calls with the provided parameter.
* </p>
*
- * @param computable
- * the computation whose results should be memorized
+ * @param computable the computation whose results should be memorized
*/
public Memoizer(final Computable<I, O> computable) {
this(computable, false);
@@ -75,48 +67,37 @@ public class Memoizer<I, O> implements Computable<I, O> {
/**
* <p>
- * Constructs a Memoizer for the provided Computable calculation, with the
- * option of whether a Computation that experiences an error should
- * recalculate on subsequent calls or return the same cached exception.
+ * Constructs a Memoizer for the provided Computable calculation, with the option of whether a Computation that
+ * experiences an error should recalculate on subsequent calls or return the same cached exception.
* </p>
*
- * @param computable
- * the computation whose results should be memorized
- * @param recalculate
- * determines whether the computation should be recalculated on
- * subsequent calls if the previous call failed
+ * @param computable the computation whose results should be memorized
+ * @param recalculate determines whether the computation should be recalculated on subsequent calls if the previous call
+ * failed
*/
public Memoizer(final Computable<I, O> computable, final boolean recalculate) {
- this.computable = computable;
this.recalculate = recalculate;
+ this.mappingFunction = k -> FutureTasks.run(() -> computable.compute(k));
}
/**
* <p>
- * This method will return the result of the calculation and cache it, if it
- * has not previously been calculated.
+ * This method will return the result of the calculation and cache it, if it has not previously been calculated.
* </p>
* <p>
- * This cache will also cache exceptions that occur during the computation
- * if the {@code recalculate} parameter in the constructor was set to
- * {@code false}, or not set. Otherwise, if an exception happened on the
- * previous calculation, the method will attempt again to generate a value.
+ * This cache will also cache exceptions that occur during the computation if the {@code recalculate} parameter in the
+ * constructor was set to {@code false}, or not set. Otherwise, if an exception happened on the previous calculation,
+ * the method will attempt again to generate a value.
* </p>
*
- * @param arg
- * the argument for the calculation
+ * @param arg the argument for the calculation
* @return the result of the calculation
- * @throws InterruptedException
- * thrown if the calculation is interrupted
+ * @throws InterruptedException thrown if the calculation is interrupted
*/
@Override
public O compute(final I arg) throws InterruptedException {
while (true) {
- Future<O> future = cache.computeIfAbsent(arg, k -> {
- final FutureTask<O> futureTask = new FutureTask<>(() -> computable.compute(arg));
- futureTask.run();
- return futureTask;
- });
+ final Future<O> future = cache.computeIfAbsent(arg, mappingFunction);
try {
return future.get();
} catch (final CancellationException e) {
@@ -132,12 +113,11 @@ public class Memoizer<I, O> implements Computable<I, O> {
/**
* <p>
- * This method launders a Throwable to either a RuntimeException, Error or
- * any other Exception wrapped in an IllegalStateException.
+ * This method launders a Throwable to either a RuntimeException, Error or any other Exception wrapped in an
+ * IllegalStateException.
* </p>
*
- * @param throwable
- * the throwable to laundered
+ * @param throwable the throwable to laundered
* @return a RuntimeException, Error or an IllegalStateException
*/
private RuntimeException launderException(final Throwable throwable) {
diff --git a/src/test/java/org/apache/commons/lang3/concurrent/FutureTasksTest.java b/src/test/java/org/apache/commons/lang3/concurrent/FutureTasksTest.java
new file mode 100644
index 0000000..da3fcd1
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/concurrent/FutureTasksTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.concurrent;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link FutureTask}.
+ */
+public class FutureTasksTest {
+
+ @Test
+ public void testRun() throws InterruptedException, ExecutionException {
+ final String data = "Hello";
+ final FutureTask<String> f = FutureTasks.run(() -> data);
+ assertTrue(f.isDone());
+ assertEquals(data, f.get());
+ }
+
+}