5 changed files with 225 additions and 14 deletions
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* Copyright 2002-present the original author or authors. |
||||
* |
||||
* Licensed 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 |
||||
* |
||||
* https://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.springframework.core.task; |
||||
|
||||
import java.util.concurrent.Callable; |
||||
|
||||
/** |
||||
* Variant of {@link Callable} with a flexible exception signature |
||||
* that can be adapted in the {@link SyncTaskExecutor#execute(TaskCallback)} |
||||
* method signature for propagating specific exception types only. |
||||
* |
||||
* <p>An implementation of this interface can also be passed into any |
||||
* {@code Callback}-based method such as {@link AsyncTaskExecutor#submit(Callable)} |
||||
* or {@link AsyncTaskExecutor#submitCompletable(Callable)}. It is just capable |
||||
* of adapting to flexible exception propagation in caller signatures as well. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 7.0 |
||||
* @param <V> the returned value type, if any |
||||
* @param <E> the exception propagated, if any |
||||
* @see SyncTaskExecutor#execute(TaskCallback) |
||||
*/ |
||||
public interface TaskCallback<V, E extends Exception> extends Callable<V> { |
||||
|
||||
@Override |
||||
V call() throws E; |
||||
|
||||
} |
||||
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
/* |
||||
* Copyright 2002-present the original author or authors. |
||||
* |
||||
* Licensed 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 |
||||
* |
||||
* https://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.springframework.core.task; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.concurrent.CompletableFuture; |
||||
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIOException; |
||||
import static org.assertj.core.api.Assertions.assertThatNoException; |
||||
|
||||
/** |
||||
* @author Juergen Hoeller |
||||
* @since 7.0 |
||||
*/ |
||||
class SyncTaskExecutorTests { |
||||
|
||||
@Test |
||||
void plainExecution() { |
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor(); |
||||
|
||||
ConcurrentClass target = new ConcurrentClass(); |
||||
assertThatNoException().isThrownBy(() -> taskExecutor.execute(target::concurrentOperation)); |
||||
assertThat(taskExecutor.execute(target::concurrentOperationWithResult)).isEqualTo("result"); |
||||
assertThatIOException().isThrownBy(() -> taskExecutor.execute(target::concurrentOperationWithException)); |
||||
} |
||||
|
||||
@Test |
||||
void withConcurrencyLimit() { |
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor(); |
||||
taskExecutor.setConcurrencyLimit(2); |
||||
|
||||
ConcurrentClass target = new ConcurrentClass(); |
||||
List<CompletableFuture<?>> futures = new ArrayList<>(10); |
||||
for (int i = 0; i < 10; i++) { |
||||
futures.add(CompletableFuture.runAsync(() -> taskExecutor.execute(target::concurrentOperation))); |
||||
} |
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); |
||||
assertThat(target.current).hasValue(0); |
||||
assertThat(target.counter).hasValue(10); |
||||
} |
||||
|
||||
@Test |
||||
void withConcurrencyLimitAndResult() { |
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor(); |
||||
taskExecutor.setConcurrencyLimit(2); |
||||
|
||||
ConcurrentClass target = new ConcurrentClass(); |
||||
List<CompletableFuture<?>> futures = new ArrayList<>(10); |
||||
for (int i = 0; i < 10; i++) { |
||||
futures.add(CompletableFuture.runAsync(() -> |
||||
assertThat(taskExecutor.execute(target::concurrentOperationWithResult)).isEqualTo("result"))); |
||||
} |
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); |
||||
assertThat(target.current).hasValue(0); |
||||
assertThat(target.counter).hasValue(10); |
||||
} |
||||
|
||||
@Test |
||||
void withConcurrencyLimitAndException() { |
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor(); |
||||
taskExecutor.setConcurrencyLimit(2); |
||||
|
||||
ConcurrentClass target = new ConcurrentClass(); |
||||
List<CompletableFuture<?>> futures = new ArrayList<>(10); |
||||
for (int i = 0; i < 10; i++) { |
||||
futures.add(CompletableFuture.runAsync(() -> |
||||
assertThatIOException().isThrownBy(() -> taskExecutor.execute(target::concurrentOperationWithException)))); |
||||
} |
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); |
||||
assertThat(target.current).hasValue(0); |
||||
assertThat(target.counter).hasValue(10); |
||||
} |
||||
|
||||
|
||||
static class ConcurrentClass { |
||||
|
||||
final AtomicInteger current = new AtomicInteger(); |
||||
|
||||
final AtomicInteger counter = new AtomicInteger(); |
||||
|
||||
public void concurrentOperation() { |
||||
if (current.incrementAndGet() > 2) { |
||||
throw new IllegalStateException(); |
||||
} |
||||
try { |
||||
Thread.sleep(10); |
||||
} |
||||
catch (InterruptedException ex) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
current.decrementAndGet(); |
||||
counter.incrementAndGet(); |
||||
} |
||||
|
||||
public String concurrentOperationWithResult() { |
||||
concurrentOperation(); |
||||
return "result"; |
||||
} |
||||
|
||||
public String concurrentOperationWithException() throws IOException { |
||||
concurrentOperation(); |
||||
throw new IOException(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue