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

com.senzing.util.AsyncWorkerPool Maven / Gradle / Ivy

The newest version!
package com.senzing.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * Provides a pool of worker threads to execute asynchronous tasks and
 * provide the results.
 *
 * @param  The return type from the asynchronous task.
 */
public class AsyncWorkerPool {
  /**
   * The list of available {@link AsyncWorker} instances.
   */
  private final List> available;

  /**
   * The list of all {@link AsyncWorker} instances whether available or not.
   */
  private List> allThreads;

  /**
   * Flag indicating if the pool has been marked closed.
   */
  private boolean closed;

  /**
   * A task to be executed.
   */
  public interface Task {
    T execute() throws Exception;
  }

  /**
   * The asynchronous result associated with a previously executed task.
   */
  public static class AsyncResult {
    private T value;
    private Exception failure;

    private AsyncResult(T value, Exception failure) {
      this.value      = value;
      this.failure    = failure;
    }

    /**
     * Returns the value produced by the completed task.
     * If an exception is thrown by the task and it does not complete
     * then this throws the associated failure.
     *
     * @return The value produced by the completed task.
     * @throws Exception If a failure occurs.
     */
    public T getValue() throws Exception {
      if (this.failure != null) throw this.failure;
      return this.value;
    }

    /**
     * Returns a diagnostic string describing this instance.
     *
     * @return A diagnostic string describing this instance.
     */
    public String toString() {
      return "{ value=[ " + this.value + " ]"
              + ((this.failure != null)
                  ? ", failure=[ " + this.failure + " ]" : "")
              + " }";
    }
  }

  /**
   * Constructs with the specified number of threads in the pool.
   * @param size The number of threads to create in the pool.
   */
  public AsyncWorkerPool(int size) {
    this("AsyncWorker", size);
  }

  /**
   * Constructs with the specified thread base name and the number of threads
   * to create.
   *
   * @param baseName The base name to use as a prefix when naming the
   *                 async worker threads in the pool.
   *
   * @param size The number of worker threads to create.
   */
  public AsyncWorkerPool(String baseName, int size) {
    this.available    = new LinkedList<>();
    this.allThreads   = new LinkedList<>();
    this.closed       = false;

    // if baseName ends with "-" then strip it off since we will add it back
    if (baseName.endsWith("-")) {
      baseName = baseName.substring(0, baseName.length() - 1);
    }

    int identityHashCode = System.identityHashCode(this);
    for (int index = 0; index < size; index++) {
      AsyncWorker aw = new AsyncWorker<>();
      aw.setName(baseName + "-" + identityHashCode + "-" + index);
      this.available.add(aw);
      this.allThreads.add(aw);
      aw.start();
    }
    this.allThreads = Collections.unmodifiableList(this.allThreads);
  }

  /**
   * Checks if one or more tasks are currently executing.
   *
   * @return true if one or more tasks are currently executing,
   *         otherwise false.
   */
  public boolean isBusy() {
    synchronized (this.available) {
      if (this.isClosed()) return false;
      return (this.available.size() < this.allThreads.size());
    }
  }

  /**
   * Executes the specified {@link Task} asynchronously and returns the {@link
   * AsyncResult} (if any) produced by a previous execution of the assigned
   * worker.  The returned {@link AsyncResult} is null if the assigned
   * worker's result has already been consumed or has not executed a previous
   * task.
   *
   * @param task The task to execute.
   * @return The {@link AsyncResult} of the previous
   */
  public AsyncResult execute(Task task) {
    synchronized (this.available) {
      AsyncWorker worker = null;
      // wait for an available worker
      while (worker == null && !this.isClosed()) {
        if (this.available.size() == 0) {
          try {
            this.available.wait(10000L);
          } catch (InterruptedException ignore) {
            // do nothing
          }
        }
        if (this.available.size() > 0) {
          worker = this.available.remove(0);
        }
      }

      // check if closed
      if (worker == null || this.isClosed()) {
        throw new IllegalStateException(
            "Pool closed while attempting to execute a task.");
      }

      // execute the task
      return worker.enlist(task);
    }
  }

  /**
   * Returns the size of the worker thread pool.
   *
   * @return The size of the worker thread pool.
   */
  public int size() {
    return this.allThreads.size();
  }

  /**
   * Checks if this pool has been closed.  Once closed, the pool can no longer
   * be used to execute any further tasks.
   *
   * @return true if closed, otherwise false
   */
  public boolean isClosed() {
    synchronized (this.available) {
      return this.closed;
    }
  }

  /**
   * Closes this pool so no further tasks can be executed against it.
   *
   * @return The {@link List} of non-null {@link AsyncResult} instances
   *         describing the results from the concluding tasks.
   */
  public List> close()
  {
    // mark this pool as closed and notify
    synchronized (this.available) {
      this.closed = true;
      this.available.notifyAll();
    }

    List> results = new ArrayList<>(this.allThreads.size());
    // mark all the threads complete
    for (AsyncWorker thread: this.allThreads) {
      AsyncResult result = thread.retire();
      if (result != null) {
        results.add(result);
      }
    }
    return results;
  }

  /**
   * A worker thread for performing asynchronous tasks.
   */
  private class AsyncWorker extends Thread {
    /**
     * Flag to indicate if the worker thread is complete.
     */
    private boolean complete;

    /**
     * The task to execute.
     */
    private Task currentTask = null;

    /**
     * Describes the result previous task.
     */
    private AsyncResult previousResult;

    /**
     * Default constructor.
     */
    private AsyncWorker() {
      this.previousResult = null;
      this.complete = false;
    }

    /**
     * Resets the thread so it is ready to execute the next task.
     */
    private synchronized AsyncResult reset() {
      AsyncResult prevResult = this.previousResult;
      this.currentTask    = null;
      this.previousResult = null;
      return prevResult;
    }

    /**
     * Sets the current task for the thread and returns the result from the
     * previous execution.
     */
    private synchronized AsyncResult enlist(Task task) {
      AsyncResult prevResult = this.reset();
      this.currentTask = task;
      this.notifyAll();
      return prevResult;
    }

    /**
     * Checks if the thread has been marked complete and should stop
     * processing tasks.
     *
     * @return true if this instance has been marked complete,
     *         otherwise false.
     */
    private synchronized boolean isComplete() {
      return this.complete;
    }

    /**
     * If this thread is busy, this waits for it to no longer be busy, then
     * marks this thread as completed, notifies, and joins against the thread.
     * Finally, this method returns the {@link AsyncResult} from the previous
     * task that was executed (if not yet consumed).
     */
    private AsyncResult retire() {
      synchronized (this) {
        while (this.isBusy()) {
          try {
            this.wait(2000L);
          } catch (InterruptedException ignore) {
            // do nothing
          }
        }
        this.complete = true;
        this.notifyAll();
      }
      try {
        this.join();
      } catch (InterruptedException ignore) {
        // do nothing
      }
      return this.reset();
    }

    /**
     * Checks if this thread is currently busy executing a task.
     *
     * @return true if this thread is currently busy executing a
     *         task, otherwise false.
     */
    private synchronized boolean isBusy() {
      return (this.currentTask != null);
    }

    /**
     * Implement the run method to wait for the next task and execute it.
     * This continues until this thread is marked complete.
     */
    @SuppressWarnings("unchecked")
    public void run()
    {
      AsyncWorkerPool pool = AsyncWorkerPool.this;

      // loop while not complete
      while (!this.isComplete() && !pool.isClosed()) {
        // get the next task to perform
        Task task = null;
        synchronized (this) {
          // loop while not complete and no task
          while (this.currentTask == null && !this.isComplete()) {
            try {
              this.wait(10000L);
            } catch (InterruptedException ignore) {
              // do nothing
            }
          }
          task = this.currentTask;
        }

        // check if we have a task to do and if so, get the result
        AsyncResult result = null;
        if (task != null) {
          try {
            // execute the task and record the result
            result = new AsyncResult<>(this.currentTask.execute(), null);

          } catch (Exception e) {
            // record any failure for the task
            result = new AsyncResult<>(null, e);
          }

          // update worker fields to clear the task and store the result
          synchronized (this) {
            this.currentTask = null;
            this.previousResult = result;
            // make sure to notify when done
            this.notifyAll();
          }
        }

        // return this instance to the pool
        synchronized (pool.available) {
          if (!pool.isClosed()) {
            pool.available.add(this);
            pool.available.notifyAll();
          }
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy