Class KeyDistributedExecutor

  • Direct Known Subclasses:
    KeyDistributedScheduler

    public class KeyDistributedExecutor
    extends java.lang.Object
    TaskDistributor is designed such that tasks executed on it for a given key will run in a single threaded manner. It needs a multi-threaded pool supplied to it, to then execute those tasks on. While the thread which runs those tasks may be different between multiple executions, no two tasks for the same key will ever be run in parallel.

    Because of that, it is recommended that the executor provided has as many possible threads as possible keys that could be provided to be run in parallel. If this class is starved for threads some keys may continue to process new tasks, while other keys could be starved.

    Assuming that the shared memory (any objects, primitives, etc) are only accessed through the same instance of KeyDistributedExecutor, and assuming that those variables are only accessed via the same key. Then the programmer does not need to worry about synchronization, or volatile. The KeyDistributedExecutor will ensure the happens-before relationship.

    Since:
    4.6.0 (since 1.0.0 as org.threadly.concurrent.TaskExecutorDistributor)
    • Constructor Summary

      Constructors 
      Constructor Description
      KeyDistributedExecutor​(java.util.concurrent.Executor executor)
      Constructor to use a provided executor implementation for running tasks.
      KeyDistributedExecutor​(java.util.concurrent.Executor executor, boolean accurateQueueSize)
      Constructor to use a provided executor implementation for running tasks.
      KeyDistributedExecutor​(java.util.concurrent.Executor executor, int maxTasksPerCycle)
      Constructor to use a provided executor implementation for running tasks.
      KeyDistributedExecutor​(java.util.concurrent.Executor executor, int maxTasksPerCycle, boolean accurateQueueSize)
      Constructor to use a provided executor implementation for running tasks.
    • Method Summary

      All Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      void execute​(java.lang.Object threadKey, java.lang.Runnable task)
      Provide a task to be run with a given thread key.
      java.util.concurrent.Executor getExecutor()
      Getter for the executor being used behind the scenes.
      SubmitterExecutor getExecutorForKey​(java.lang.Object threadKey)
      Returns a SubmitterExecutor implementation where all tasks submitted on this executor will run on the provided key.
      int getTaskQueueSize​(java.lang.Object threadKey)
      Call to check how many tasks have been queued up for a given key.
      java.util.Map<java.lang.Object,​java.lang.Integer> getTaskQueueSizeMap()
      Get a map of all the keys and how many tasks are queued per key.
      ListenableFuture<?> submit​(java.lang.Object threadKey, java.lang.Runnable task)
      Submit a task to be run with a given thread key.
      <T> ListenableFuture<T> submit​(java.lang.Object threadKey, java.lang.Runnable task, T result)
      Submit a task to be run with a given thread key.
      <T> ListenableFuture<T> submit​(java.lang.Object threadKey, java.util.concurrent.Callable<T> task)
      Submit a callable to be run with a given thread key.
      • Methods inherited from class java.lang.Object

        equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Constructor Detail

      • KeyDistributedExecutor

        public KeyDistributedExecutor​(java.util.concurrent.Executor executor)
        Constructor to use a provided executor implementation for running tasks.

        This constructs with a default expected level of concurrency of 16. This also does not attempt to have an accurate queue size for the getTaskQueueSize(Object) call (thus preferring high performance).

        Parameters:
        executor - A multi-threaded executor to distribute tasks to. Ideally has as many possible threads as keys that will be used in parallel.
      • KeyDistributedExecutor

        public KeyDistributedExecutor​(java.util.concurrent.Executor executor,
                                      boolean accurateQueueSize)
        Constructor to use a provided executor implementation for running tasks.

        This constructor allows you to specify if you want accurate queue sizes to be tracked for given thread keys. There is a performance hit associated with this, so this should only be enabled if getTaskQueueSize(Object) calls will be used.

        This constructs with a default expected level of concurrency of 16.

        Parameters:
        executor - A multi-threaded executor to distribute tasks to. Ideally has as many possible threads as keys that will be used in parallel.
        accurateQueueSize - true to make getTaskQueueSize(Object) more accurate
      • KeyDistributedExecutor

        public KeyDistributedExecutor​(java.util.concurrent.Executor executor,
                                      int maxTasksPerCycle)
        Constructor to use a provided executor implementation for running tasks.

        This constructor allows you to provide a maximum number of tasks for a key before it yields to another key. This can make it more fair, and make it so no single key can starve other keys from running. The lower this is set however, the less efficient it becomes in part because it has to give up the thread and get it again, but also because it must copy the subset of the task queue which it can run.

        This also allows you to specify if you want accurate queue sizes to be tracked for given thread keys. There is a performance hit associated with this, so this should only be enabled if getTaskQueueSize(Object) calls will be used.

        This constructs with a default expected level of concurrency of 16. This also does not attempt to have an accurate queue size for the "getTaskQueueSize" call (thus preferring high performance).

        Parameters:
        executor - A multi-threaded executor to distribute tasks to. Ideally has as many possible threads as keys that will be used in parallel.
        maxTasksPerCycle - maximum tasks run per key before yielding for other keys
      • KeyDistributedExecutor

        public KeyDistributedExecutor​(java.util.concurrent.Executor executor,
                                      int maxTasksPerCycle,
                                      boolean accurateQueueSize)
        Constructor to use a provided executor implementation for running tasks.

        This constructor allows you to provide a maximum number of tasks for a key before it yields to another key. This can make it more fair, and make it so no single key can starve other keys from running. The lower this is set however, the less efficient it becomes in part because it has to give up the thread and get it again, but also because it must copy the subset of the task queue which it can run.

        This also allows you to specify if you want accurate queue sizes to be tracked for given thread keys. There is a performance hit associated with this, so this should only be enabled if getTaskQueueSize(Object) calls will be used.

        This constructs with a default expected level of concurrency of 16.

        Parameters:
        executor - A multi-threaded executor to distribute tasks to. Ideally has as many possible threads as keys that will be used in parallel.
        maxTasksPerCycle - maximum tasks run per key before yielding for other keys
        accurateQueueSize - true to make getTaskQueueSize(Object) more accurate
    • Method Detail

      • getExecutor

        public java.util.concurrent.Executor getExecutor()
        Getter for the executor being used behind the scenes.
        Returns:
        executor tasks are being distributed to
      • getExecutorForKey

        public SubmitterExecutor getExecutorForKey​(java.lang.Object threadKey)
        Returns a SubmitterExecutor implementation where all tasks submitted on this executor will run on the provided key.
        Parameters:
        threadKey - object key where hashCode will be used to determine execution thread
        Returns:
        executor which will only execute based on the provided key
      • getTaskQueueSize

        public int getTaskQueueSize​(java.lang.Object threadKey)
        Call to check how many tasks have been queued up for a given key. Depending on what constructor was used, and if a true was passed in for accurateQueueSize, the accuracy of this call varies dramatically.

        If true was not supplied in the constructor for accurateQueueSize, this will only report how many tasks have not been accepted by the worker yet. The accepting of those tasks occur in batches, so this number will vary dramatically (and probably be unusable).

        So it is highly recommended that if your interested in this functionality you supply a true into the constructor.

        Supplying a true for accurateQueueSize in the constructor does involve some performance cost, but that overhead should be minimal (just no reason to accept any loss if not interested in this feature).

        Parameters:
        threadKey - key for task queue to examine
        Returns:
        the number of tasks queued for the key
        Since:
        1.2.0
      • getTaskQueueSizeMap

        public java.util.Map<java.lang.Object,​java.lang.Integer> getTaskQueueSizeMap()
        Get a map of all the keys and how many tasks are queued per key. This map is generated without locking. Due to that, this may be inaccurate as task queue sizes changed while iterating all key's active workers.

        Because this requires an iteration of all task workers, if only a single key's queue size is needed, use getTaskQueueSize(Object) as a cheaper alternative.

        If true was not supplied in the constructor for accurateQueueSize, this will only report how many tasks have not been accepted by the worker yet. The accepting of those tasks occur in batches, so this number will vary dramatically (and probably be unusable).

        So it is highly recommended that if your interested in this functionality you supply a true into the constructor.

        Supplying a true for accurateQueueSize in the constructor does involve some performance cost, but that overhead should be minimal (just no reason to accept any loss if not interested in this feature).

        Returns:
        Map of task key's to their respective queue size
      • execute

        public void execute​(java.lang.Object threadKey,
                            java.lang.Runnable task)
        Provide a task to be run with a given thread key.
        Parameters:
        threadKey - object key where equals() will be used to determine execution thread
        task - Task to be executed
      • submit

        public ListenableFuture<?> submit​(java.lang.Object threadKey,
                                          java.lang.Runnable task)
        Submit a task to be run with a given thread key.
        Parameters:
        threadKey - object key where equals() will be used to determine execution thread
        task - Task to be executed
        Returns:
        Future to represent when the execution has occurred
      • submit

        public <T> ListenableFuture<T> submit​(java.lang.Object threadKey,
                                              java.lang.Runnable task,
                                              T result)
        Submit a task to be run with a given thread key.
        Type Parameters:
        T - type of result returned from the future
        Parameters:
        threadKey - object key where equals() will be used to determine execution thread
        task - Runnable to be executed
        result - Result to be returned from future when task completes
        Returns:
        Future to represent when the execution has occurred and provide the given result
      • submit

        public <T> ListenableFuture<T> submit​(java.lang.Object threadKey,
                                              java.util.concurrent.Callable<T> task)
        Submit a callable to be run with a given thread key.
        Type Parameters:
        T - type of result returned from the future
        Parameters:
        threadKey - object key where equals() will be used to determine execution thread
        task - Callable to be executed
        Returns:
        Future to represent when the execution has occurred and provide the result from the callable