Runs multiple jobs in parallel : Scheduling « Threads « Java

Home
Java
1.2D Graphics GUI
2.3D
3.Advanced Graphics
4.Ant
5.Apache Common
6.Chart
7.Class
8.Collections Data Structure
9.Data Type
10.Database SQL JDBC
11.Design Pattern
12.Development Class
13.EJB3
14.Email
15.Event
16.File Input Output
17.Game
18.Generics
19.GWT
20.Hibernate
21.I18N
22.J2EE
23.J2ME
24.JDK 6
25.JNDI LDAP
26.JPA
27.JSP
28.JSTL
29.Language Basics
30.Network Protocol
31.PDF RTF
32.Reflection
33.Regular Expressions
34.Scripting
35.Security
36.Servlets
37.Spring
38.Swing Components
39.Swing JFC
40.SWT JFace Eclipse
41.Threads
42.Tiny Application
43.Velocity
44.Web Services SOA
45.XML
Java » Threads » SchedulingScreenshots 
Runs multiple jobs in parallel
 


/*
 * Runs multiple jobs in parallel
 * Copyright (C) 2004-2005 Matt Conway
 * http://simplygenius.com/
 * Copyright (C) 2005 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * See COPYING.TXT for details.
 */


import java.util.*;

/**
 * Runs multiple jobs in parallel, n threads at a time, and waits
 * until all threads are complete before continuing.
 * <p>
 * Typically, Parallelizer would be used to run each of the items-
 * in a for loop at the same time.  For example the following for
 * loop:
 * <pre>
 * for (int i=0; i<10; i++){
 *    System.out.println("Hello World " + i);
 * }
 * System.out.println("done");
 * </pre>
 * To this:
 * <pre>
 * Parallelizer parallelizer = new Parallelizer();
 * for (int i=0; i<10; i++){
 *     final int j = i;
 *     parallelizer.run(
 *         new Runnable(){
 *             System.out.println("Hello World " + j);
 *         }
 *     );
 * }
 * parallelizer.join();
 * System.out.println("done");
 *
 * More information about this class is available from <a target="_top" href=
 * "http://ostermiller.org/utils/Parallelizer.html">ostermiller.org</a>.
 *
 @author Matt Conway - http://simplygenius.com/
 @author Stephen Ostermiller - http://ostermiller.org/contact.pl?regarding=Java+Utilities
 @since ostermillerutils 1.05.00
 */
public class Parallelizer
{
  /**
   * Constant that may be passed concurrentThreadLimit argument
   * of the constructor indicating that no limit should be placed
   * on the number of threads that are allowed to run concurrently.
   *
   @since ostermillerutils 1.05.00
   */
  public static final int INFINITE_THREAD_LIMIT = 0;

  /**
   * The number of threads that are allowed to be run concurrently.
   * (INFINITE_THREAD_LIMIT for no limit)
   */
  private int concurrentThreadLimit = INFINITE_THREAD_LIMIT;

  /**
   * Create a new Parallelizer with no limit on the number
   * of threads that will be allowed to be run concurrently.
   *
   @since ostermillerutils 1.05.00
   */
  public Parallelizer(){
    this(INFINITE_THREAD_LIMIT);
  }

  /**
   * Create a new Parallelizer with the specified limit on the number
   * of threads that will be allowed to be run concurrently.
   * <p>
   * When the concurrent thread limit is reached and the parallelizer
   * gets a new thread to run, the new thread will be queued until
   * a thread finishes.
   *
   @param concurrentThreadLimit number of threads that will be allowed
   *     to run simultaneously or INFINITE_THREAD_LIMIT for no limit.
   @throws IllegalArgumentException if concurrentThreadLimit not a whole
   *     number or INFINITE_THREAD_LIMIT
   *
   @since ostermillerutils 1.05.00
   */
  public Parallelizer(int concurrentThreadLimit){
    if (concurrentThreadLimit < INFINITE_THREAD_LIMITthrow new IllegalArgumentException("Bad concurrent thread limit: " + concurrentThreadLimit);
    this.concurrentThreadLimit = concurrentThreadLimit;
  }

  /**
   * A Set of threads that are currently running.
   * This set is also used as a lock to synchronize
   * anything that touches running threads.
   */
  private HashSet<Thread> runningThreads = new HashSet<Thread>();

  /**
   * A queue of jobs that have not yet been started.
   */
  private LinkedList<Thread> toRunQueue = new LinkedList<Thread>();

  /**
   * Run the given job.  The given job is either run
   * immediately or if the max number of concurrent jobs are already
   * running, it is queued to be run when some job is finished.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @param job job which is to be run in parallel with other jobs.
   @throws Error if any thread that is already running has thrown an Error.
   @throws NullPointerException if job is null.
   *
   @since ostermillerutils 1.05.00
   */
  public void run(Runnable job){
    run(null, job, null, 0);
  }

  /**
   * Run the given job.  The given job is either run
   * immediately or if the max number of concurrent jobs are already
   * running, it is queued to be run when some job is finished.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @param job job which is to be run in parallel with other jobs.
   @param threadName name for the thread that will be created to run the job (null for auto generated thread name)
   @throws Error if any thread that is already running has thrown an Error.
   @throws NullPointerException if job is null.
   *
   @since ostermillerutils 1.05.00
   */
  public void run(Runnable job, String threadName){
    run(null, job, threadName, 0);
  }

  /**
   * Run the given job.  The given job is either run
   * immediately or if the max number of concurrent jobs are already
   * running, it is queued to be run when some job is finished.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @param threadGroup group in which this job should be run (null for default group).
   @param job job which is to be run in parallel with other jobs.
   @throws Error if any thread that is already running has thrown an Error.
   @throws NullPointerException if job is null.
   *
   @since ostermillerutils 1.05.00
   */
  public void run(ThreadGroup threadGroup, Runnable job){
    run(threadGroup, job, null, 0);
  }

  /**
   * Run the given job.  The given job is either run
   * immediately or if the max number of concurrent jobs are already
   * running, it is queued to be run when some job is finished.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @param threadGroup group in which this job should be run (null for default group).
   @param job job which is to be run in parallel with other jobs.
   @param threadName name for the thread that will be created to run the job (null for auto generated thread name)
   @throws Error if any thread that is already running has thrown an Error.
   @throws NullPointerException if job is null.
   *
   @since ostermillerutils 1.05.00
   */
  public void run(ThreadGroup threadGroup, Runnable job, String threadName){
    run(threadGroup, job, threadName, 0);
  }

  /**
   * Run the given job.  The given job is either run
   * immediately or if the max number of concurrent jobs are already
   * running, it is queued to be run when some job is finished.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @param threadGroup group in which this job should be run (null for default group).
   @param job job which is to be run in parallel with other jobs.
   @param threadName name for the thread that will be created to run the job (null for auto generated thread name)
   @param stackSize system dependent stack size suggestion for thread creation (0 for default stack size).
   @throws Error if any thread that is already running has thrown an Error.
   @throws NullPointerException if job is null.
   *
   @since ostermillerutils 1.05.00
   */
  public void run(ThreadGroup threadGroup, final Runnable job, String threadName, long stackSize){
    throwFirstError();

    Runnable jobWrapper = new Runnable(){
      public void run(){
        try {
          job.run();
        catch (RuntimeException runtimeException){
          // Put exceptions in the exception queue
          synchronized(runningThreads){
            exceptionList.add(runtimeException);
          }
        catch (Error error){
          // Put errors in the error queue
          synchronized(runningThreads){
            errorList.add(error);
          }
        finally {
          synchronized(runningThreads){
            // when done remove ourselves from the list
            // of running threads.
            runningThreads.remove(Thread.currentThread());
            // Notify the block method.
            runningThreads.notifyAll();
          }
          // If there are jobs queued up to be run, now would
          // be a good time to run them.
          startAJobIfNeeded();
        }
      }
    };

    // ensure the thread name is not null, and auto generate a name if it is
    threadName = getNextThreadName(threadName);

    // If we are already running the max number of jobs, queue this job up
    synchronized(runningThreads){
      toRunQueue.add(
        new Thread(
          threadGroup,
          jobWrapper,
          threadName,
          stackSize
        )
      );
    }

    // Now that the job is in the queue of jobs to run,
    // check the queue and see if the job should be started
    startAJobIfNeeded();
  }

  /**
   * An number to assign to the next auto generated thread name
   */
  private static int threadNameCount = 0;

  /**
   * Ensure the given thread name is not null.  If not null, return it,
   * if it is null, then then generate a name.
   *
   @param threadName existing thread name to check
   @return the given thread name or a generated thread name if the specified name was null.
   */
  private static String getNextThreadName(String threadName){
    if (threadName != nullreturn threadName;
    return "Parallelizer-"+(threadNameCount++);
  }

  /**
   * A queue of exceptions that running threads have thrown.
   */
  private LinkedList<RuntimeException> exceptionList = new LinkedList<RuntimeException>();

  /**
   * Remove the first exception from the exception list and throw it.
   *
   @throws RuntimeException if a running thread has thrown an exception not yet thrown by this method.
   */
  private void throwFirstException(){
    synchronized(runningThreads){
      if (exceptionList.size() 0){
        throw exceptionList.removeFirst();
      }
    }
  }

  /**
   * A queue of exceptions that running threads have thrown.
   */
  private LinkedList<Error> errorList = new LinkedList<Error>();

  /**
   * Remove the first error from the error list and throw it.
   *
   @throws Error if a running thread has thrown an error not yet thrown by this method.
   */
  private void throwFirstError() throws Error {
    synchronized(runningThreads){
      if (errorList.size() 0){
        throw errorList.removeFirst();
      }
    }
  }

  /**
   * Remove a job from the toRunQueue, create a thread for it,
   * start the thread, and put the job in the set of running jobs.
   * But do all this only if there are jobs queued up to be run
   * and we are not already running the max number of concurrent
   * jobs at once.
   */
  private void startAJobIfNeeded(){
    synchronized(runningThreads){
      // If we are already running the max number of jobs, just return
      if (concurrentThreadLimit != INFINITE_THREAD_LIMIT){
        if (runningThreads.size() >= concurrentThreadLimitreturn;
      }

      // If there are no more job to run, return
      if (toRunQueue.size() == 0return;

      // Get a job out of the queue
      Thread thread = toRunQueue.removeFirst();

      // Put the thread in the list of running threads
      runningThreads.add(thread);
      thread.start();
    }
  }

  /**
   * Return true iff all jobs that have been requested to run
   * in this Parallelizer have completed.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @return Whether all jobs are done or not.
   @throws Error if any of the running threads has thrown an Error.
   *
   @since ostermillerutils 1.05.00
   */
  public boolean done(){
    throwFirstError();
    synchronized(runningThreads){
      return (toRunQueue.size() + runningThreads.size()) == 0;
    }
  }

  /**
   * All currently running threads will be interrupted.
   * The threads interrupted threads may die, causing
   * jobs that were queued but not yet started, to start.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @throws Error if any of the running threads has thrown an Error.
   *
   @since ostermillerutils 1.05.00
   */
  public void interrupt(){
    throwFirstError();
    synchronized(runningThreads){
      for (Thread thread: runningThreads) {
        (thread).interrupt();
        throwFirstError();
      }
    }
  }

  /**
   * Dump the stack of each running thread.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @throws Error if any of the running threads has thrown an Error.
   *
   @since ostermillerutils 1.05.00
   */
  public void dumpStack(){
    throwFirstError();
    synchronized(runningThreads){
      for (Thread thread: runningThreads) {
        for (StackTraceElement stackTraceElement: thread.getStackTrace()){
          System.out.println(stackTraceElement.toString());
        }
        throwFirstError();
      }
    }
  }

  /**
   * Gets a list of all running threads.  There may be jobs that
   * are queued and do not yet have threads.  These job are not
   * returned.
   * <p>
   * If this method throws an error, that
   * error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the error.
   *
   @throws Error if any of the running threads has thrown an Error.
   @return an array of all currently running threads.
   *
   @since ostermillerutils 1.05.00
   */
  public Thread[] getRunningThreads(){
    throwFirstError();
    synchronized(runningThreads){
      return runningThreads.toArray(new Thread[0]);
    }
  }

  /**
   * Block until all the jobs in this Parallelizer have run
   * and then return.
   * <p>
   * If this method throws an exception or an error, that
   * exception or error may be handled and this method
   * may be called again as it will not re-throw the same
   * instance of the exception or error.
   *
   @throws InterruptedException if interrupted while waiting.
   @throws RuntimeException any running thread throws or has thrown a runtime exception.
   @throws Error if any of the running threads throws or has thrown an Error.
   *
   @since ostermillerutils 1.05.00
   */
  public void join() throws InterruptedException {
    while (!done()){
      synchronized(runningThreads){
        throwFirstException();
        runningThreads.wait();
        throwFirstError();
        throwFirstException();
      }
    }
  }
}

   
  
Related examples in the same category
1.Task SchedulingTask Scheduling
2.Job SchedulerJob Scheduler
3.Executes a task with a specified timeout
4.Timer class used to implement login and query timeouts
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.