All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.threadly.concurrent.wrapper.limiter.SubmitterSchedulerLimiter Maven / Gradle / Ivy

package org.threadly.concurrent.wrapper.limiter;

import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;

import org.threadly.concurrent.RunnableCallableAdapter;
import org.threadly.concurrent.RunnableContainer;
import org.threadly.concurrent.SubmitterScheduler;
import org.threadly.concurrent.future.ListenableFuture;
import org.threadly.concurrent.future.ListenableFutureTask;
import org.threadly.util.ArgumentVerifier;
import org.threadly.util.Clock;

/**
 * This class is designed to limit how much parallel execution happens on a provided 
 * {@link SubmitterScheduler}.  This allows the implementor to have one thread pool for all 
 * their code, and if they want certain sections to have less levels of parallelism (possibly 
 * because those those sections would completely consume the global pool), they can wrap the 
 * executor in this class.
 * 

* Thus providing you better control on the absolute thread count and how much parallelism can * occur in different sections of the program. *

* This is an alternative from having to create multiple thread pools. By using this you also are * able to accomplish more efficiently thread use than multiple thread pools would. *

* If limiting to a single thread, please see {@link SingleThreadSchedulerSubPool} as a possible * alternative. * * @since 4.6.0 (since 4.3.0 at org.threadly.concurrent.limiter) */ public class SubmitterSchedulerLimiter extends ExecutorLimiter implements SubmitterScheduler { protected final SubmitterScheduler scheduler; /** * Constructs a new limiter that implements the {@link SubmitterScheduler}. * * @param scheduler {@link SubmitterScheduler} implementation to submit task executions to. * @param maxConcurrency maximum quantity of runnables to run in parallel */ public SubmitterSchedulerLimiter(SubmitterScheduler scheduler, int maxConcurrency) { this(scheduler, maxConcurrency, DEFAULT_LIMIT_FUTURE_LISTENER_EXECUTION); } /** * Constructs a new limiter that implements the {@link SubmitterScheduler}. *

* This constructor allows you to specify if listeners / * {@link org.threadly.concurrent.future.FutureCallback}'s / functions in * {@link ListenableFuture#map(java.util.function.Function)} or * {@link ListenableFuture#flatMap(java.util.function.Function)} should be counted towards the * concurrency limit. Specifying {@code false} will release the limit as soon as the original * task completes. Specifying {@code true} will continue to enforce the limit until all listeners * (without an executor) complete. * * @param scheduler {@link SubmitterScheduler} implementation to submit task executions to. * @param maxConcurrency maximum quantity of runnables to run in parallel * @param limitFutureListenersExecution {@code true} to include listener / mapped functions towards execution limit */ public SubmitterSchedulerLimiter(SubmitterScheduler scheduler, int maxConcurrency, boolean limitFutureListenersExecution) { super(scheduler, maxConcurrency, limitFutureListenersExecution); this.scheduler = scheduler; } @Override public ListenableFuture submitScheduled(Runnable task, long delayInMs) { return submitScheduled(task, null, delayInMs); } @Override public ListenableFuture submitScheduled(Runnable task, T result, long delayInMs) { return submitScheduled(RunnableCallableAdapter.adapt(task, result), delayInMs); } @Override public ListenableFuture submitScheduled(Callable task, long delayInMs) { ArgumentVerifier.assertNotNull(task, "task"); ListenableFutureTask ft = new ListenableFutureTask<>(task, this); doSchedule(ft, ft, delayInMs); return ft; } /** * Adds a task to either execute (delay zero), or schedule with the provided delay. No safety * checks are done at this point, so only provide non-null inputs. * * @param task Task for execution * @param delayInMs delay in milliseconds, greater than or equal to zero */ protected void doSchedule(Runnable task, ListenableFuture future, long delayInMs) { if (delayInMs == 0) { executeOrQueue(task, future); } else { scheduler.schedule(new DelayedExecutionRunnable(task), delayInMs); } } @Override public void schedule(Runnable task, long delayInMs) { ArgumentVerifier.assertNotNull(task, "task"); ArgumentVerifier.assertNotNegative(delayInMs, "delayInMs"); doSchedule(task, null, delayInMs); } protected void initialRecurringSchedule(RecurringWrapper rw, long initialDelay) { ArgumentVerifier.assertNotNegative(initialDelay, "initialDelay"); if (initialDelay == 0) { executeOrQueueWrapper(rw); } else { scheduler.schedule(rw.delayRunnable, initialDelay); } } @Override public void scheduleWithFixedDelay(Runnable task, long initialDelay, long recurringDelay) { ArgumentVerifier.assertNotNull(task, "task"); ArgumentVerifier.assertNotNegative(recurringDelay, "recurringDelay"); initialRecurringSchedule(new RecurringDelayWrapper(task, recurringDelay), initialDelay); } @Override public void scheduleAtFixedRate(Runnable task, long initialDelay, long period) { ArgumentVerifier.assertNotNull(task, "task"); ArgumentVerifier.assertGreaterThanZero(period, "period"); initialRecurringSchedule(new RecurringRateWrapper(task, initialDelay, period), initialDelay); } /** * Small runnable that allows scheduled tasks to pass through the same execution queue that * immediate execution has to. * * @since 1.1.0 */ protected class DelayedExecutionRunnable implements Runnable, RunnableContainer { private final LimiterRunnableWrapper lrw; protected DelayedExecutionRunnable(Runnable runnable) { this(new LimiterRunnableWrapper(runnable)); } protected DelayedExecutionRunnable(LimiterRunnableWrapper lrw) { this.lrw = lrw; } @Override public void run() { if (canRunTask()) { // we can run in the thread we already have lrw.run(); } else { addToQueue(lrw); } } @Override public Runnable getContainedRunnable() { return lrw.getContainedRunnable(); } } /** * Abstract class to represent a wrapper for a recurring task. The primary need for this is to * deal with the removal of these recurring tasks in the {@link SchedulerServiceLimiter}. * * @since 5.15 */ protected abstract class RecurringWrapper extends LimiterRunnableWrapper { protected final AtomicBoolean valid; protected final DelayedExecutionRunnable delayRunnable; public RecurringWrapper(Runnable runnable) { super(runnable); valid = new AtomicBoolean(true); delayRunnable = new DelayedExecutionRunnable(this); } public boolean invalidate() { return valid.compareAndSet(true, false); } @Override public void run() { if (valid.get()) { super.run(); } } } /** * Wrapper for recurring tasks that reschedule with a given delay after completing execution. * * @since 3.1.0 */ protected class RecurringDelayWrapper extends RecurringWrapper { protected final long recurringDelay; public RecurringDelayWrapper(Runnable runnable, long recurringDelay) { super(runnable); this.recurringDelay = recurringDelay; } @Override protected void doAfterRunTasks() { scheduler.schedule(delayRunnable, recurringDelay); } } /** * Wrapper for recurring tasks that reschedule at a fixed rate after completing execution. * * @since 3.1.0 */ protected class RecurringRateWrapper extends RecurringWrapper { protected final long period; private long nextRunTime; public RecurringRateWrapper(Runnable runnable, long initialDelay, long period) { super(runnable); this.period = period; nextRunTime = Clock.accurateForwardProgressingMillis() + initialDelay + period; } @Override protected void doAfterRunTasks() { nextRunTime += period; long nextDelay = nextRunTime - Clock.accurateForwardProgressingMillis(); if (nextDelay < 1) { executeOrQueueWrapper(this); } else { scheduler.schedule(delayRunnable, nextDelay); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy