Class AdaptiveExecutionStrategy

  • All Implemented Interfaces:
    Container, Destroyable, Dumpable, Dumpable.DumpableContainer, LifeCycle, ExecutionStrategy
    Direct Known Subclasses:
    EatWhatYouKill

    @ManagedObject("Adaptive execution strategy")
    public class AdaptiveExecutionStrategy
    extends ContainerLifeCycle
    implements ExecutionStrategy

    An adaptive execution strategy that uses the Invocable status of both the task and the current thread to select an optimal strategy that prioritizes executing the task immediately in the current producing thread if it can be done so without thread starvation issues.

    This strategy selects between the following sub-strategies:

    ProduceConsume(PC)
    The producing thread consumes the task by running it directly and then continues to produce.
    ProduceInvokeConsume(PIC)
    The producing thread consumes the task by running it with Invocable.invokeNonBlocking(Runnable) and then continues to produce.
    ProduceExecuteConsume(PEC)
    The producing thread dispatches the task to a thread pool to be executed and then continues to produce.
    ExecuteProduceConsume(EPC)
    The producing thread consumes dispatches a pending producer to a thread pool, then consumes the task by running it directly (as in PC mode), then races with the pending producer thread to take over production.

    The sub-strategy is selected as follows:

    PC
    If the produced task is Invocable.InvocationType.NON_BLOCKING.
    EPC
    If the producing thread is not Invocable.InvocationType.NON_BLOCKING and a pending producer thread is available, either because there is already a pending producer or one is successfully started with TryExecutor.tryExecute(Runnable).
    PIC
    If the produced task is Invocable.InvocationType.EITHER and EPC was not selected.
    PEC
    Otherwise.

    Because of the preference for PC mode, on a multicore machine with many many Invocable.InvocationType.NON_BLOCKING tasks, multiple instances of the strategy may be required to keep all CPUs on the system busy.

    Since the producing thread may be invoked with Invocable.invokeNonBlocking(Runnable) this allows AdaptiveExecutionStrategys to be efficiently and safely chained: a task produced by one execution strategy may become itself be a producer in a second execution strategy (e.g. an IO selector may use an execution strategy to handle multiple connections and each connection may use a execution strategy to handle multiplexed channels/streams within the connection).

    A task containing another AdaptiveExecutionStrategy should identify as Invocable.InvocationType.EITHER so when there are no pending producers threads available to the first strategy, then it may invoke the second as Invocable.InvocationType.NON_BLOCKING. This avoids starvation as the production on the second strategy can always be executed, but without the risk that it may block the last available producer for the first strategy.

    This strategy was previously named EatWhatYouKill (EWYK) because its preference for a producer to directly consume the tasks that it produces is similar to a hunting proverb that says that a hunter should eat (i.e. consume) what they kill (i.e. produced).

    • Constructor Detail

      • AdaptiveExecutionStrategy

        public AdaptiveExecutionStrategy​(ExecutionStrategy.Producer producer,
                                         java.util.concurrent.Executor executor)
        Parameters:
        producer - The produce of tasks to be consumed.
        executor - The executor to be used for executing producers or consumers, depending on the sub-strategy.