Update to 1.0.4

This commit is contained in:
Andrea Cavalli 2020-08-24 23:48:05 +02:00
parent 8da67564c5
commit 435fb8eb0f
12 changed files with 681 additions and 117 deletions

View File

@ -7,7 +7,7 @@
<artifactId>common-utils</artifactId> <artifactId>common-utils</artifactId>
<groupId>org.warp</groupId> <groupId>org.warp</groupId>
<version>1.0.3</version> <version>1.0.4</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@ -16,9 +16,15 @@ public class ParallelUtils {
public static <K, V> void parallelize(Consumer<BiConsumer<K, V>> iterator, public static <K, V> void parallelize(Consumer<BiConsumer<K, V>> iterator,
int maxQueueSize, int maxQueueSize,
int parallelism, int parallelism,
int groupSize, int groupSize, BiConsumer<K, V> consumer) {
BiConsumer<K, V> consumer) { var parallelExecutor = BoundedExecutorService.create(maxQueueSize,
BoundedExecutorService parallelExecutor = BoundedExecutorService.create(maxQueueSize, parallelism, parallelism, 0, TimeUnit.MILLISECONDS, new ShortNamedThreadFactory("ForEachParallel"), (a, b) -> {}); parallelism,
parallelism,
0,
TimeUnit.MILLISECONDS,
new ShortNamedThreadFactory("ForEachParallel"),
(a, b) -> {}
);
final int CHUNK_SIZE = groupSize; final int CHUNK_SIZE = groupSize;
IntWrapper count = new IntWrapper(CHUNK_SIZE); IntWrapper count = new IntWrapper(CHUNK_SIZE);
VariableWrapper<Object[]> keys = new VariableWrapper<>(new Object[CHUNK_SIZE]); VariableWrapper<Object[]> keys = new VariableWrapper<>(new Object[CHUNK_SIZE]);
@ -58,7 +64,14 @@ public class ParallelUtils {
int parallelism, int parallelism,
int groupSize, int groupSize,
TriConsumer<K1, K2, V> consumer) { TriConsumer<K1, K2, V> consumer) {
BoundedExecutorService parallelExecutor = BoundedExecutorService.create(maxQueueSize, parallelism, parallelism, 0, TimeUnit.MILLISECONDS, new ShortNamedThreadFactory("ForEachParallel"), (a, b) -> {}); var parallelExecutor = BoundedExecutorService.create(maxQueueSize,
parallelism,
parallelism,
0,
TimeUnit.MILLISECONDS,
new ShortNamedThreadFactory("ForEachParallel"),
(a, b) -> {}
);
final int CHUNK_SIZE = groupSize; final int CHUNK_SIZE = groupSize;
IntWrapper count = new IntWrapper(CHUNK_SIZE); IntWrapper count = new IntWrapper(CHUNK_SIZE);
VariableWrapper<Object[]> keys1 = new VariableWrapper<>(new Object[CHUNK_SIZE]); VariableWrapper<Object[]> keys1 = new VariableWrapper<>(new Object[CHUNK_SIZE]);

View File

@ -0,0 +1,229 @@
package org.warp.commonutils.concurrency.executor;
import java.lang.StackWalker.StackFrame;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.jetbrains.annotations.NotNull;
import org.warp.commonutils.type.IntWrapper;
public class AsyncStackTraceExecutorDecorator extends ExecutorDecorator {
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private static final Map<Thread, LSTTask> threadToTask = new HashMap<>();
private static final Map<Thread, AsyncStackTraceExecutorDecorator> threadToExecutor = new HashMap<>();
public AsyncStackTraceExecutorDecorator(Executor executor) {
super(executor);
}
@Override
public void execute(@NotNull Runnable command) {
var currentThread = Thread.currentThread();
List<StackFrame> frames = new ArrayList<>();
LSTTask lstTask;
lock.readLock().lock();
try {
lstTask = threadToTask.getOrDefault(currentThread, null);
// Add the current stack frames
addCurrentStackTrace(frames, lstTask != null);
if (lstTask != null) {
frames.addAll(lstTask.frames);
}
lstTask = new LSTTask(command, frames);
//System.out.println("execute(): THREAD-" + Thread.currentThread().hashCode() + " TASK-" + lstTask.hashCode());
} finally {
lock.readLock().unlock();
}
super.execute(lstTask);
}
private static void addCurrentStackTrace(List<StackFrame> frames, boolean isFromAsyncCal) {
StackWalker.getInstance().walk(a -> {
IntWrapper count = new IntWrapper(0);
int STACK_MAX_SIZE = 10;
a.filter(x -> {
var cn = x.getClassName();
return !cn.equals("java.util.concurrent.CompletableFuture")
&& !cn.equals("java.util.concurrent.CompletableFuture$AsyncRun")
&& !cn.equals("java.util.concurrent.ThreadPoolExecutor")
&& !cn.equals("java.util.concurrent.ThreadPoolExecutor$Worker")
&& !cn.equals("java.lang.Thread")
&& !cn.equals(LSTTask.class.getName())
&& !cn.equals(AsyncStackTraceExecutorDecorator.class.getName());
}).skip(0).limit(STACK_MAX_SIZE + 1).peek(x -> count.var++).forEachOrdered(frames::add);
if (count.var > STACK_MAX_SIZE) {
frames.remove(frames.size() - 1);
frames.add(new TextStackFrame("AndMoreFrames"));
}
return null;
});
if (isFromAsyncCal) {
frames.add(new TextStackFrame("AsyncCall"));
}
}
class LSTTask implements Runnable {
private final Runnable runnable;
List<StackFrame> frames;
LSTTask(Runnable runnable, List<StackFrame> frames) {
this.runnable = runnable;
this.frames = frames;
}
@Override
public void run() {
var currentThread = Thread.currentThread();
lock.writeLock().lock();
try {
threadToTask.put(currentThread, LSTTask.this);
threadToExecutor.put(currentThread, AsyncStackTraceExecutorDecorator.this);
} finally {
lock.writeLock().unlock();
}
try {
//System.out.println(" run(): THREAD-" + Thread.currentThread().hashCode() + " TASK-" + this.hashCode());
runnable.run();
} catch (Throwable t) {
RuntimeException e = new RuntimeException(t);
e.setStackTrace(frames.stream().map(StackFrame::toStackTraceElement).toArray(StackTraceElement[]::new));
throw e;
}
lock.writeLock().lock();
try {
threadToExecutor.remove(currentThread, AsyncStackTraceExecutorDecorator.this);
threadToTask.remove(currentThread, LSTTask.this);
} finally {
lock.writeLock().unlock();
}
}
}
public static void fixStackTrace(Exception ex) {
List<StackTraceElement> result = new ArrayList<>();
var currentThread = Thread.currentThread();
lock.readLock().lock();
try {
var executor = threadToExecutor.getOrDefault(currentThread, null);
if (executor != null) {
LSTTask lstTask = threadToTask.getOrDefault(currentThread, null);
if (lstTask != null) {
var currentStackFrames = new ArrayList<StackFrame>();
addCurrentStackTrace(currentStackFrames, true);
for (var frame : currentStackFrames) {
result.add(frame.toStackTraceElement());
}
for (var frame : lstTask.frames) {
result.add(frame.toStackTraceElement());
}
ex.setStackTrace(result.toArray(StackTraceElement[]::new));
}
}
} finally {
lock.readLock().unlock();
}
}
public static void dumpStack() {
var currentThread = Thread.currentThread();
lock.readLock().lock();
try {
var executor = threadToExecutor.getOrDefault(currentThread, null);
if (executor != null) {
LSTTask lstTask = threadToTask.getOrDefault(currentThread, null);
if (lstTask != null) {
StringBuilder sb = new StringBuilder();
sb.append(new Exception("Stack trace").toString()).append('\n');
var currentStackFrames = new ArrayList<StackFrame>();
addCurrentStackTrace(currentStackFrames, true);
for (var frame : currentStackFrames) {
printStackFrame(sb, frame);
}
for (var frame : lstTask.frames) {
printStackFrame(sb, frame);
}
System.err.println(sb.toString());
return;
}
}
Thread.dumpStack();
} finally {
lock.readLock().unlock();
}
}
private static void printStackFrame(StringBuilder sb, StackFrame frame) {
if(frame.getClassName().equals("AsyncCall")) {
sb.append("\t(async call)\n");
} else if(frame.getClassName().equals("AndMoreFrames")) {
sb.append("\t... omitted more frames\n");
} else {
sb.append("\tat ").append(frame.toString()).append('\n');
}
}
private static class TextStackFrame implements StackFrame {
private final String text;
public TextStackFrame(String text) {
this.text = text;
}
@Override
public String getClassName() {
return text;
}
@Override
public String getMethodName() {
return "..";
}
@Override
public Class<?> getDeclaringClass() {
return Object.class;
}
@Override
public int getByteCodeIndex() {
return 0;
}
@Override
public String getFileName() {
return null;
}
@Override
public int getLineNumber() {
return 0;
}
@Override
public boolean isNativeMethod() {
return false;
}
@Override
public StackTraceElement toStackTraceElement() {
return new StackTraceElement(getClassName(), getMethodName(), getFileName(), getLineNumber());
}
}
}

View File

@ -7,100 +7,24 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.StampedLock;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecutorService { public class BlockingOnFullQueueExecutorServiceDecorator extends ExecutorServiceDecorator {
private final void updateQueue() {
var queueSize = queueSizeSupplier.get();
synchronized (queueSizeStatusLock) {
if (queueSizeStatus != null) queueSizeStatus.accept(queueSize >= maximumTaskNumber, queueSize);
}
};
private static final class PermitReleasingRunnableDecorator implements Runnable {
@Nonnull
private final Runnable delegate;
@Nonnull
private final Runnable queueSizeUpdater;
@Nonnull
private final Semaphore semaphore;
private PermitReleasingRunnableDecorator(@Nonnull final Runnable task, @Nonnull final Runnable queueSizeUpdater, @Nonnull final Semaphore semaphoreToRelease) {
this.delegate = task;
this.queueSizeUpdater = queueSizeUpdater;
this.semaphore = semaphoreToRelease;
}
@Override
public void run() {
try {
queueSizeUpdater.run();
} finally {
// however execution goes, release permit for next task
this.semaphore.release();
this.delegate.run();
}
}
@Override
public final String toString() {
return String.format("%s[delegate='%s']", getClass().getSimpleName(), this.delegate);
}
}
private static final class PermitReleasingCallableDecorator<T> implements Callable<T> {
@Nonnull
private final Callable<T> delegate;
@Nonnull
private final Runnable queueSizeUpdater;
@Nonnull
private final Semaphore semaphore;
private PermitReleasingCallableDecorator(@Nonnull final Callable<T> task, @Nonnull final Runnable queueSizeUpdater, @Nonnull final Semaphore semaphoreToRelease) {
this.delegate = task;
this.queueSizeUpdater = queueSizeUpdater;
this.semaphore = semaphoreToRelease;
}
@Override
public T call() throws Exception {
try {
queueSizeUpdater.run();
} finally {
// however execution goes, release permit for next task
this.semaphore.release();
return this.delegate.call();
}
}
@Override
public final String toString() {
return String.format("%s[delegate='%s']", getClass().getSimpleName(), this.delegate);
}
}
private volatile boolean ignoreTaskLimit; private volatile boolean ignoreTaskLimit;
private final StampedLock drainAllLock = new StampedLock();
@Nonnull @Nonnull
private final Semaphore taskLimit; private final Semaphore taskLimit;
@ -117,11 +41,9 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
@Nonnull @Nonnull
private final Object queueSizeStatusLock; private final Object queueSizeStatusLock;
@Nonnull
private final ExecutorService delegate;
public BlockingOnFullQueueExecutorServiceDecorator(@Nonnull final ExecutorService executor, final int maximumTaskNumber, @Nonnull final Duration maximumTimeout, @Nonnull Supplier<Integer> queueSizeSupplier, @Nullable BiConsumer<Boolean, Integer> queueSizeStatus) { public BlockingOnFullQueueExecutorServiceDecorator(@Nonnull final ExecutorService executor, final int maximumTaskNumber, @Nonnull final Duration maximumTimeout, @Nonnull Supplier<Integer> queueSizeSupplier, @Nullable BiConsumer<Boolean, Integer> queueSizeStatus) {
this.delegate = Objects.requireNonNull(executor, "'executor' must not be null"); super(executor);
ExecutorServiceDecorator.hasDecorator(executor, this.getClass());
if (maximumTaskNumber < 0) { if (maximumTaskNumber < 0) {
throw new IllegalArgumentException(String.format("At least zero tasks must be permitted, not '%d'", maximumTaskNumber)); throw new IllegalArgumentException(String.format("At least zero tasks must be permitted, not '%d'", maximumTaskNumber));
} else if (maximumTaskNumber == 0) { } else if (maximumTaskNumber == 0) {
@ -138,6 +60,18 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
this.taskLimit = new Semaphore(maximumTaskNumber); this.taskLimit = new Semaphore(maximumTaskNumber);
} }
public BlockingOnFullQueueExecutorServiceDecorator(@Nonnull final ExecutorService executor, final int maximumTaskNumber, @Nonnull final Duration maximumTimeout, @Nonnull Supplier<Integer> queueSizeSupplier) {
this(executor, maximumTaskNumber, maximumTimeout, queueSizeSupplier, null);
}
private void updateQueue() {
var queueSize = queueSizeSupplier.get();
synchronized (queueSizeStatusLock) {
if (queueSizeStatus != null) queueSizeStatus.accept(queueSize >= maximumTaskNumber, queueSize);
}
}
private void preExecute(Object command) { private void preExecute(Object command) {
Objects.requireNonNull(command, "'command' must not be null"); Objects.requireNonNull(command, "'command' must not be null");
if (!ignoreTaskLimit) { if (!ignoreTaskLimit) {
@ -152,7 +86,7 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
} }
// attempt to acquire permit for task execution // attempt to acquire permit for task execution
if (!this.taskLimit.tryAcquire(this.timeout.toMillis(), MILLISECONDS)) { if (!this.taskLimit.tryAcquire(this.timeout.toMillis(), MILLISECONDS)) {
throw new RejectedExecutionException(String.format("Executor '%s' busy", this.delegate)); throw new RejectedExecutionException(String.format("Executor '%s' busy", super.toString()));
} }
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
// restore interrupt status // restore interrupt status
@ -163,10 +97,10 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
} }
@Override @Override
public final void execute(final Runnable command) { public final void execute(final @NotNull Runnable command) {
preExecute(command); preExecute(command);
this.delegate.execute(new PermitReleasingRunnableDecorator(command, () -> { super.execute(new PermitReleasingRunnableDecorator(command, () -> {
var queueSize = queueSizeSupplier.get(); var queueSize = queueSizeSupplier.get();
synchronized (queueSizeStatusLock) { synchronized (queueSizeStatusLock) {
if (queueSizeStatus != null) queueSizeStatus.accept(!ignoreTaskLimit && queueSize >= maximumTaskNumber, queueSize); if (queueSizeStatus != null) queueSizeStatus.accept(!ignoreTaskLimit && queueSize >= maximumTaskNumber, queueSize);
@ -181,7 +115,7 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
while (this.taskLimit.hasQueuedThreads()) { while (this.taskLimit.hasQueuedThreads()) {
this.taskLimit.release(10); this.taskLimit.release(10);
} }
this.delegate.shutdown(); super.shutdown();
} }
@NotNull @NotNull
@ -191,22 +125,22 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
while (this.taskLimit.hasQueuedThreads()) { while (this.taskLimit.hasQueuedThreads()) {
this.taskLimit.release(10); this.taskLimit.release(10);
} }
return this.delegate.shutdownNow(); return super.shutdownNow();
} }
@Override @Override
public boolean isShutdown() { public boolean isShutdown() {
return this.delegate.isShutdown(); return super.isShutdown();
} }
@Override @Override
public boolean isTerminated() { public boolean isTerminated() {
return this.delegate.isTerminated(); return super.isTerminated();
} }
@Override @Override
public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException { public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
return this.delegate.awaitTermination(timeout, unit); return super.awaitTermination(timeout, unit);
} }
@NotNull @NotNull
@ -214,7 +148,7 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
public <T> Future<T> submit(@NotNull Callable<T> task) { public <T> Future<T> submit(@NotNull Callable<T> task) {
preExecute(task); preExecute(task);
return this.delegate.submit(new PermitReleasingCallableDecorator<>(task, this::updateQueue, this.taskLimit)); return super.submit(new PermitReleasingCallableDecorator<>(task, this::updateQueue, this.taskLimit));
} }
@NotNull @NotNull
@ -222,7 +156,7 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
public <T> Future<T> submit(@NotNull Runnable task, T result) { public <T> Future<T> submit(@NotNull Runnable task, T result) {
preExecute(task); preExecute(task);
return this.delegate.submit(new PermitReleasingRunnableDecorator(task, this::updateQueue, this.taskLimit), result); return super.submit(new PermitReleasingRunnableDecorator(task, this::updateQueue, this.taskLimit), result);
} }
@NotNull @NotNull
@ -230,12 +164,12 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
public Future<?> submit(@NotNull Runnable task) { public Future<?> submit(@NotNull Runnable task) {
preExecute(task); preExecute(task);
return this.delegate.submit(new PermitReleasingRunnableDecorator(task, this::updateQueue, this.taskLimit)); return super.submit(new PermitReleasingRunnableDecorator(task, this::updateQueue, this.taskLimit));
} }
@NotNull @NotNull
@Override @Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException { public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks) {
throw new UnsupportedOperationException("invokeAll(tasks) is not supported"); throw new UnsupportedOperationException("invokeAll(tasks) is not supported");
} }
@ -243,26 +177,24 @@ public class BlockingOnFullQueueExecutorServiceDecorator implements BoundedExecu
@Override @Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks, public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks,
long timeout, long timeout,
@NotNull TimeUnit unit) throws InterruptedException { @NotNull TimeUnit unit) {
throw new UnsupportedOperationException("invokeAll(tasks, timeout, unit) is not supported"); throw new UnsupportedOperationException("invokeAll(tasks, timeout, unit) is not supported");
} }
@NotNull @NotNull
@Override @Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks) public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks) {
throws InterruptedException, ExecutionException {
throw new UnsupportedOperationException("invokeAny(tasks) is not supported"); throw new UnsupportedOperationException("invokeAny(tasks) is not supported");
} }
@Override @Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) {
throws InterruptedException, ExecutionException, TimeoutException {
throw new UnsupportedOperationException("invokeAny(tasks, timeout, unit) is not supported"); throw new UnsupportedOperationException("invokeAny(tasks, timeout, unit) is not supported");
} }
@Override @Override
public final String toString() { public final String toString() {
return String.format("%s[availablePermits='%s',timeout='%s',delegate='%s']", getClass().getSimpleName(), this.taskLimit.availablePermits(), return String.format("%s[availablePermits='%s',timeout='%s',delegate='%s']", getClass().getSimpleName(), this.taskLimit.availablePermits(),
this.timeout, this.delegate); this.timeout, super.toString());
} }
} }

View File

@ -1,6 +1,7 @@
package org.warp.commonutils.concurrency.executor; package org.warp.commonutils.concurrency.executor;
import java.time.Duration; import java.time.Duration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@ -10,10 +11,14 @@ import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public interface BoundedExecutorService extends ExecutorService { public class BoundedExecutorService {
private BoundedExecutorService() {
}
@Deprecated @Deprecated
static ExecutorService createUnbounded( public static ExecutorService createUnbounded(
int corePoolSize, int corePoolSize,
int maxPoolSize, int maxPoolSize,
long keepAliveTime, long keepAliveTime,
@ -22,18 +27,29 @@ public interface BoundedExecutorService extends ExecutorService {
return create(0, corePoolSize, maxPoolSize, keepAliveTime, unit, Executors.defaultThreadFactory(), queueSizeStatus); return create(0, corePoolSize, maxPoolSize, keepAliveTime, unit, Executors.defaultThreadFactory(), queueSizeStatus);
} }
static ExecutorService createUnbounded( public static ExecutorService createUnbounded(
int corePoolSize, int corePoolSize,
int maxPoolSize, int maxPoolSize,
long keepAliveTime, long keepAliveTime,
TimeUnit unit, TimeUnit unit,
ThreadFactory threadFactory, ThreadFactory threadFactory,
@Nullable BiConsumer<Boolean, Integer> queueSizeStatus) { @Nullable BiConsumer<Boolean, Integer> queueSizeStatus) {
return create(0, corePoolSize, maxPoolSize, keepAliveTime, unit, threadFactory, Duration.ofDays(100000), queueSizeStatus); return createCustom(0, corePoolSize, maxPoolSize, keepAliveTime, unit, threadFactory, Duration.ofDays(100000), queueSizeStatus, new LinkedBlockingQueue<>());
}
public static ExecutorService createUnbounded(
int corePoolSize,
int maxPoolSize,
long keepAliveTime,
TimeUnit unit,
ThreadFactory threadFactory,
@Nullable BiConsumer<Boolean, Integer> queueSizeStatus,
BlockingQueue<Runnable> queue) {
return createCustom(0, corePoolSize, maxPoolSize, keepAliveTime, unit, threadFactory, Duration.ofDays(100000), queueSizeStatus, queue);
} }
@Deprecated @Deprecated
static BoundedExecutorService create( public static ExecutorService create(
int maxQueueSize, int maxQueueSize,
int corePoolSize, int corePoolSize,
int maxPoolSize, int maxPoolSize,
@ -43,7 +59,7 @@ public interface BoundedExecutorService extends ExecutorService {
return create(maxQueueSize, corePoolSize, maxPoolSize, keepAliveTime, unit, Executors.defaultThreadFactory(), queueSizeStatus); return create(maxQueueSize, corePoolSize, maxPoolSize, keepAliveTime, unit, Executors.defaultThreadFactory(), queueSizeStatus);
} }
static BoundedExecutorService create( public static ExecutorService create(
int maxQueueSize, int maxQueueSize,
int corePoolSize, int corePoolSize,
int maxPoolSize, int maxPoolSize,
@ -51,10 +67,22 @@ public interface BoundedExecutorService extends ExecutorService {
TimeUnit unit, TimeUnit unit,
ThreadFactory threadFactory, ThreadFactory threadFactory,
@Nullable BiConsumer<Boolean, Integer> queueSizeStatus) { @Nullable BiConsumer<Boolean, Integer> queueSizeStatus) {
return create(maxQueueSize, corePoolSize, maxPoolSize, keepAliveTime, unit, threadFactory, Duration.ofDays(100000), queueSizeStatus); return createCustom(maxQueueSize, corePoolSize, maxPoolSize, keepAliveTime, unit, threadFactory, Duration.ofDays(100000), queueSizeStatus, new LinkedBlockingQueue<>());
} }
static BoundedExecutorService create( public static ExecutorService create(
int maxQueueSize,
int corePoolSize,
int maxPoolSize,
long keepAliveTime,
TimeUnit unit,
ThreadFactory threadFactory,
@Nullable BiConsumer<Boolean, Integer> queueSizeStatus,
BlockingQueue<Runnable> queue) {
return createCustom(maxQueueSize, corePoolSize, maxPoolSize, keepAliveTime, unit, threadFactory, Duration.ofDays(100000), queueSizeStatus, queue);
}
public static ExecutorService createCustom(
int maxQueueSize, int maxQueueSize,
int corePoolSize, int corePoolSize,
int maxPoolSize, int maxPoolSize,
@ -62,8 +90,8 @@ public interface BoundedExecutorService extends ExecutorService {
TimeUnit unit, TimeUnit unit,
ThreadFactory threadFactory, ThreadFactory threadFactory,
Duration queueItemTtl, Duration queueItemTtl,
@Nullable BiConsumer<Boolean, Integer> queueSizeStatus) { @Nullable BiConsumer<Boolean, Integer> queueSizeStatus,
var queue = new LinkedBlockingQueue<Runnable>(); BlockingQueue<Runnable> queue) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
maxPoolSize, maxPoolSize,
keepAliveTime, keepAliveTime,

View File

@ -0,0 +1,18 @@
package org.warp.commonutils.concurrency.executor;
import java.util.Objects;
import java.util.concurrent.Callable;
public abstract class CallableDecorator<T> implements Callable<T> {
private final Callable<T> callable;
public CallableDecorator(Callable<T> callable) {
this.callable = Objects.requireNonNull(callable);
}
@Override
public T call() throws Exception {
return callable.call();
}
}

View File

@ -0,0 +1,30 @@
package org.warp.commonutils.concurrency.executor;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import org.jetbrains.annotations.NotNull;
public abstract class ExecutorDecorator implements Executor {
private final Executor executor;
public ExecutorDecorator(Executor executor) {
this.executor = Objects.requireNonNull(executor);
}
public final Set<Class<? extends ExecutorDecorator>> getExecutorDecorators() {
if (executor instanceof ExecutorDecorator) {
var decorators = ((ExecutorDecorator) executor).getExecutorDecorators();
decorators.add(this.getClass());
return decorators;
} else {
return new HashSet<>();
}
}
@Override
public void execute(@NotNull Runnable runnable) {
executor.execute(runnable);
}
}

View File

@ -0,0 +1,119 @@
package org.warp.commonutils.concurrency.executor;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jetbrains.annotations.NotNull;
public abstract class ExecutorServiceDecorator implements ExecutorService {
private ExecutorService executorService;
public ExecutorServiceDecorator(ExecutorService executorService) {
this.executorService = Objects.requireNonNull(executorService);
}
protected static boolean hasDecorator(ExecutorService executor,
Class<? extends ExecutorServiceDecorator> decoratorClass) {
if (executor instanceof ExecutorServiceDecorator) {
var executorServiceDecoratorImpl = (ExecutorServiceDecorator) executor;
var executorServiceDecorators = executorServiceDecoratorImpl.getExecutorServiceDecorators();
return executorServiceDecorators.contains(decoratorClass);
}
return false;
}
public final Set<Class<? extends ExecutorServiceDecorator>> getExecutorServiceDecorators() {
if (executorService instanceof ExecutorServiceDecorator) {
var decorators = ((ExecutorServiceDecorator) executorService).getExecutorServiceDecorators();
decorators.add(this.getClass());
return decorators;
} else {
return new HashSet<>();
}
}
@Override
public void shutdown() {
executorService.shutdown();
}
@NotNull
@Override
public List<Runnable> shutdownNow() {
return executorService.shutdownNow();
}
@Override
public boolean isShutdown() {
return executorService.isShutdown();
}
@Override
public boolean isTerminated() {
return executorService.isTerminated();
}
@Override
public boolean awaitTermination(long l, @NotNull TimeUnit timeUnit) throws InterruptedException {
return executorService.awaitTermination(l, timeUnit);
}
@NotNull
@Override
public <T> Future<T> submit(@NotNull Callable<T> callable) {
return executorService.submit(callable);
}
@NotNull
@Override
public <T> Future<T> submit(@NotNull Runnable runnable, T t) {
return executorService.submit(runnable, t);
}
@NotNull
@Override
public Future<?> submit(@NotNull Runnable runnable) {
return executorService.submit(runnable);
}
@NotNull
@Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> collection)
throws InterruptedException {
return executorService.invokeAll(collection);
}
@NotNull
@Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> collection,
long l,
@NotNull TimeUnit timeUnit) throws InterruptedException {
return executorService.invokeAll(collection, l, timeUnit);
}
@NotNull
@Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> collection)
throws InterruptedException, ExecutionException {
return executorService.invokeAny(collection);
}
@Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> collection, long l, @NotNull TimeUnit timeUnit)
throws InterruptedException, ExecutionException, TimeoutException {
return executorService.invokeAny(collection, l, timeUnit);
}
@Override
public void execute(@NotNull Runnable runnable) {
executorService.execute(runnable);
}
}

View File

@ -0,0 +1,39 @@
package org.warp.commonutils.concurrency.executor;
import java.util.concurrent.Callable;
import java.util.concurrent.Semaphore;
import javax.annotation.Nonnull;
public final class PermitReleasingCallableDecorator<T> extends CallableDecorator<T> {
@Nonnull
private final Runnable queueSizeUpdater;
@Nonnull
private final Semaphore semaphore;
PermitReleasingCallableDecorator(@Nonnull final Callable<T> task,
@Nonnull final Runnable queueSizeUpdater,
@Nonnull final Semaphore semaphoreToRelease) {
super(task);
this.queueSizeUpdater = queueSizeUpdater;
this.semaphore = semaphoreToRelease;
}
@Override
public T call() throws Exception {
try {
queueSizeUpdater.run();
} finally {
// however execution goes, release permit for next task
this.semaphore.release();
return super.call();
}
}
@Override
public final String toString() {
return String.format("%s[delegate='%s']", getClass().getSimpleName(), super.toString());
}
}

View File

@ -0,0 +1,38 @@
package org.warp.commonutils.concurrency.executor;
import java.util.concurrent.Semaphore;
import javax.annotation.Nonnull;
public final class PermitReleasingRunnableDecorator extends RunnableDecorator {
@Nonnull
private final Runnable queueSizeUpdater;
@Nonnull
private final Semaphore semaphore;
PermitReleasingRunnableDecorator(@Nonnull final Runnable task,
@Nonnull final Runnable queueSizeUpdater,
@Nonnull final Semaphore semaphoreToRelease) {
super(task);
this.queueSizeUpdater = queueSizeUpdater;
this.semaphore = semaphoreToRelease;
}
@Override
public void run() {
try {
queueSizeUpdater.run();
} finally {
// however execution goes, release permit for next task
this.semaphore.release();
super.run();
}
}
@Override
public final String toString() {
return String.format("%s[delegate='%s']", getClass().getSimpleName(), super.toString());
}
}

View File

@ -0,0 +1,17 @@
package org.warp.commonutils.concurrency.executor;
import java.util.Objects;
public abstract class RunnableDecorator implements Runnable {
private final Runnable runnable;
public RunnableDecorator(Runnable runnable) {
this.runnable = Objects.requireNonNull(runnable);
}
@Override
public void run() {
runnable.run();
}
}

View File

@ -0,0 +1,101 @@
package org.warp.commonutils.concurrency.executor;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
public class SimplerExecutorServiceDecorator extends ExecutorServiceDecorator {
private final ExecutorDecorator executorDecorator;
public SimplerExecutorServiceDecorator(ExecutorService executorService,
Function<Executor, ExecutorDecorator> executorDecoratorInitializer) {
super(executorService);
this.executorDecorator = executorDecoratorInitializer.apply(executorService);
}
@Override
public void shutdown() {
super.shutdown();
}
@NotNull
@Override
public List<Runnable> shutdownNow() {
return super.shutdownNow();
}
@Override
public boolean isShutdown() {
return super.isShutdown();
}
@Override
public boolean isTerminated() {
return super.isTerminated();
}
@Override
public boolean awaitTermination(long l, @NotNull TimeUnit timeUnit) throws InterruptedException {
return super.awaitTermination(l, timeUnit);
}
@NotNull
@Override
public <T> Future<T> submit(@NotNull Callable<T> callable) {
return super.submit(callable);
}
@NotNull
@Override
public <T> Future<T> submit(@NotNull Runnable runnable, T t) {
return super.submit(runnable, t);
}
@NotNull
@Override
public Future<?> submit(@NotNull Runnable runnable) {
return super.submit(runnable);
}
@NotNull
@Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> collection)
throws InterruptedException {
return super.invokeAll(collection);
}
@NotNull
@Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> collection,
long l,
@NotNull TimeUnit timeUnit) throws InterruptedException {
return super.invokeAll(collection, l, timeUnit);
}
@NotNull
@Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> collection)
throws InterruptedException, ExecutionException {
return super.invokeAny(collection);
}
@Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> collection, long l, @NotNull TimeUnit timeUnit)
throws InterruptedException, ExecutionException, TimeoutException {
return super.invokeAny(collection, l, timeUnit);
}
@Override
public void execute(@NotNull Runnable runnable) {
executorDecorator.execute(runnable);
}
}