Java/Threads/Scheduling

Материал из Java эксперт
Перейти к: навигация, поиск

Executes a task with a specified timeout

 
/*
 * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/util/TimeoutController.java,v 1.6 2004/04/18 23:51:38 jsdever Exp $
 * $Revision: 480424 $
 * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
 *
 * 
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

/**
 * <p>
 * Executes a task with a specified timeout.
 * </p>
 * @author Ortwin Glueck
 * @author 
 * @version $Revision: 480424 $
 * @since 2.0
 */
public final class TimeoutController {
    /**
     * Do not instantiate objects of this class. Methods are static.
     */
    private TimeoutController() {
    }
    /**
     * Executes <code>task</code>. Waits for <code>timeout</code>
     * milliseconds for the task to end and returns. If the task does not return
     * in time, the thread is interrupted and an Exception is thrown.
     * The caller should override the Thread.interrupt() method to something that
     * quickly makes the thread die or use Thread.isInterrupted().
     * @param task The thread to execute
     * @param timeout The timeout in milliseconds. 0 means to wait forever.
     * @throws TimeoutException if the timeout passes and the thread does not return.
     */
    public static void execute(Thread task, long timeout) throws TimeoutException {
        task.start();
        try {
            task.join(timeout);
        } catch (InterruptedException e) {
            /* if somebody interrupts us he knows what he is doing */
        }
        if (task.isAlive()) {
            task.interrupt();
            throw new TimeoutException();
        }
    }
    /**
     * Executes <code>task</code> in a new deamon Thread and waits for the timeout.
     * @param task The task to execute
     * @param timeout The timeout in milliseconds. 0 means to wait forever.
     * @throws TimeoutException if the timeout passes and the thread does not return.
     */
    public static void execute(Runnable task, long timeout) throws TimeoutException {
        Thread t = new Thread(task, "Timeout guard");
        t.setDaemon(true);
        execute(t, timeout);
    }
    /**
     * Signals that the task timed out.
     */
    public static class TimeoutException extends Exception {
        /** Create an instance */
        public TimeoutException() {
        }
    }
}





Job Scheduler

 
/*
 *
 * Copyright (c) 1997-1999 Scott Oaks and Henry Wong. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and
 * without fee is hereby granted.
 *
 * This sample source code is provided for example only,
 * on an unsupported, as-is basis. 
 *
 * AUTHOR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. AUTHOR SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  AUTHOR
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 */
import java.util.*;
public class JobScheduler implements Runnable {
  final public static int ONCE = 1;
  final public static int FOREVER = -1;
  final public static long HOURLY = (long)60*60*1000;
  final public static long DAILY = 24*HOURLY;
  final public static long WEEKLY = 7*DAILY;
  final public static long MONTHLY = -1;
  final public static long YEARLY = -2;
  private class JobNode {
    public Runnable job;
    public Date executeAt;
    public long interval;
    public int count;
  }
  private ThreadPool tp;
  private DaemonLock dlock = new DaemonLock();
  private Vector jobs = new Vector(100);
  public JobScheduler(int poolSize) {
    tp = (poolSize > 0) ? new ThreadPool(poolSize) : null;
    Thread js = new Thread(this);
    js.setDaemon(true);
    js.start();
  }
  private synchronized void addJob(JobNode job) {
    dlock.acquire();
    jobs.addElement(job);
    notify();
  }
  private synchronized void deleteJob(Runnable job) {
    for (int i=0; i < jobs.size(); i++) {
      if (((JobNode) jobs.elementAt(i)).job == job) {
        jobs.removeElementAt(i);
        dlock.release();
        break;
      }
    }
  }
  private JobNode updateJobNode(JobNode jn) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(jn.executeAt);
    if (jn.interval == MONTHLY) {
        // There is a minor bug. (see java.util.calendar)
        cal.add(Calendar.MONTH, 1);
        jn.executeAt = cal.getTime();
    } else if (jn.interval == YEARLY) {
        cal.add(Calendar.YEAR, 1);
        jn.executeAt = cal.getTime();
    } else {
        jn.executeAt = new Date(jn.executeAt.getTime() + jn.interval);
    }
    jn.count = (jn.count == FOREVER) ? FOREVER : jn.count - 1;
    return (jn.count != 0) ? jn : null;
  }
  private synchronized long runJobs() {
    long minDiff = Long.MAX_VALUE;
    long now = System.currentTimeMillis();
    for (int i=0; i < jobs.size();) {
      JobNode jn = (JobNode) jobs.elementAt(i);
      if (jn.executeAt.getTime() <= now) {
        if (tp != null) {
          tp.addRequest(jn.job);
        } else {
          Thread jt = new Thread(jn.job);
          jt.setDaemon(false);
          jt.start();
        }
        if (updateJobNode(jn) == null) {
          jobs.removeElementAt(i);
          dlock.release();
        }
      } else {
        long diff = jn.executeAt.getTime() - now;
        minDiff = Math.min(diff, minDiff);
        i++;
      }
    }
    return minDiff;
  }
  public synchronized void run() {
    while (true) {
      long waitTime = runJobs();
      try {
        wait(waitTime);
      } catch (Exception e) {};
    }
  }
  public void execute(Runnable job) {
    executeIn(job, (long)0);
  }
  public void executeIn(Runnable job, long millis) {
    executeInAndRepeat(job, millis, 1000, ONCE);
  }
  public void executeInAndRepeat(Runnable job, long millis, long repeat) {
    executeInAndRepeat(job, millis, repeat, FOREVER);
  }
  public void executeInAndRepeat(Runnable job, long millis, long repeat, int count) {
    Date when = new Date(System.currentTimeMillis() + millis);
    executeAtAndRepeat(job, when, repeat, count);
  }
  public void executeAt(Runnable job, Date when) {
    executeAtAndRepeat(job, when, 1000, ONCE);
  }
  public void executeAtAndRepeat(Runnable job, Date when, long repeat) {
    executeAtAndRepeat(job, when, repeat, FOREVER); 
  }
  public void executeAtAndRepeat(Runnable job, Date when, long repeat, int count) {
    JobNode jn = new JobNode();
    jn.job = job;
    jn.executeAt = when;
    jn.interval = repeat;
    jn.count = count;
    addJob(jn);
  }
  public void cancel(Runnable job) {
    deleteJob(job);
  }
  public void executeAtNextDOW(Runnable job, Date when, int DOW) {
    Calendar target = Calendar.getInstance();
    target.setTime(when);
    while (target.get(Calendar.DAY_OF_WEEK) != DOW)
      target.add(Calendar.DATE, 1);
    executeAt(job, target.getTime());
  }
  public void configureBackup(Runnable job) {
    Calendar now = Calendar.getInstance();
    executeAtNextDOW(job, now.getTime(), Calendar.SUNDAY);
  }
  public static void main(String[] args)
    throws Exception {
    Runnable r1 = new Runnable() {
      public void run() {
        System.out.print("1");
        try { Thread.sleep(5000); } catch (Exception ex) {};
        System.out.print("1");
      }
    };
    Runnable r2 = new Runnable() {
      public void run() {
        System.out.print("2");
        try { Thread.sleep(5000); } catch (Exception ex) {};
        System.out.print("2");
      }
    };
    Runnable r3 = new Runnable() {
      public void run() {
        System.out.print("3");
      }
    };
    Runnable r4 = new Runnable() {
      public void run() {
        System.out.print("4");
      }
    };
    JobScheduler js = new JobScheduler(0);
    Thread.sleep(1000);
    // Test 1 - General Test
    js.executeInAndRepeat(r1, 10000, 3000, 10); 
    js.executeInAndRepeat(r2, 20000, 1000, 10); 
    //Thread.sleep(11000);
    //js.cancel(r1);
    //js.cancel(r2);
    //js.configureBackup(r1);
    // Test 2 - Signature Test
    //Date in10Sec = new Date(System.currentTimeMillis()+10000L);
    //js.execute(r1);
    //js.executeIn(r2, 2000L);
    //js.executeInAndRepeat(r3, 10000L, 2000L);
    //js.executeInAndRepeat(r4, 10000L, 2000L, 5);
    //js.executeAt(r1, in10Sec);
    //js.executeAtAndRepeat(r2, in10Sec, 2000L);
    //js.executeAtAndRepeat(r3, in10Sec, 1000L, 5);
    //js.cancel(r4);
    //Thread.sleep(20000L);
    //js.cancel(r2);
    // Test 3 - Interval Test
    //js.executeInAndRepeat(r3, 10000L, JobScheduler.HOURLY);
    //js.executeInAndRepeat(r3, 10000L, JobScheduler.DAILY);
    //js.executeInAndRepeat(r3, 10000L, JobScheduler.WEEKLY);
    //js.executeInAndRepeat(r3, 10000L, JobScheduler.MONTHLY);
    //js.executeInAndRepeat(r3, 10000L, JobScheduler.YEARLY);
  }
}





Runs multiple jobs in parallel

 

/*
 * Runs multiple jobs in parallel
 * Copyright (C) 2004-2005 Matt Conway
 * http://simplygenius.ru/
 * 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 .
 *
 * @author Matt Conway - http://simplygenius.ru/
 * @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_LIMIT) throw 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 != null) return 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() >= concurrentThreadLimit) return;
      }
      // If there are no more job to run, return
      if (toRunQueue.size() == 0) return;
      // 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();
      }
    }
  }
}





Task Scheduling

 
/*
Java Threads, 3rd Edition
By Scott Oaks, Henry Wong
3rd Edition September 2004 
ISBN: 0-596-00782-5
*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class URLMonitorPanel extends JPanel implements URLPingTask.URLUpdate {
  Timer timer;
  URL url;
  URLPingTask task;
  JPanel status;
  JButton startButton, stopButton;
  public URLMonitorPanel(String url, Timer t) throws MalformedURLException {
    setLayout(new BorderLayout());
    timer = t;
    this.url = new URL(url);
    add(new JLabel(url), BorderLayout.CENTER);
    JPanel temp = new JPanel();
    status = new JPanel();
    status.setSize(20, 20);
    temp.add(status);
    startButton = new JButton("Start");
    startButton.setEnabled(false);
    startButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        makeTask();
        startButton.setEnabled(false);
        stopButton.setEnabled(true);
      }
    });
    stopButton = new JButton("Stop");
    stopButton.setEnabled(true);
    stopButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        task.cancel();
        startButton.setEnabled(true);
        stopButton.setEnabled(false);
      }
    });
    temp.add(startButton);
    temp.add(stopButton);
    add(temp, BorderLayout.EAST);
    makeTask();
  }
  private void makeTask() {
    task = new URLPingTask(url, this);
    timer.schedule(task, 0L, 5000L);
  }
  public void isAlive(final boolean b) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        status.setBackground(b ? Color.GREEN : Color.RED);
        status.repaint();
      }
    });
  }
  public static void main(String[] args) throws Exception {
    JFrame frame = new JFrame("URL Monitor");
    Container c = frame.getContentPane();
    c.setLayout(new BoxLayout(c, BoxLayout.Y_AXIS));
    Timer t = new Timer();
    String[] u = new String[]{"http://www.jexp.ru","http://www.jexp.ru"};
    
    for (int i = 0; i < u.length; i++) {
      c.add(new URLMonitorPanel(u[i], t));
    }
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        System.exit(0);
      }
    });
    frame.pack();
    frame.show();
  }
}
class URLPingTask extends TimerTask {
  public interface URLUpdate {
    public void isAlive(boolean b);
  }
  URL url;
  URLUpdate updater;
  public URLPingTask(URL url) {
    this(url, null);
  }
  public URLPingTask(URL url, URLUpdate uu) {
    this.url = url;
    updater = uu;
  }
  public void run() {
    if (System.currentTimeMillis() - scheduledExecutionTime() > 5000) {
      // Let the next task do it
      return;
    }
    try {
      HttpURLConnection huc = (HttpURLConnection) url.openConnection();
      huc.setConnectTimeout(1000);
      huc.setReadTimeout(1000);
      int code = huc.getResponseCode();
      if (updater != null)
        updater.isAlive(true);
    } catch (Exception e) {
      if (updater != null)
        updater.isAlive(false);
    }
  }
}





Timer class used to implement login and query timeouts

 
// jTDS JDBC Driver for Microsoft SQL Server and Sybase
// Copyright (C) 2004 The jTDS Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
import java.util.LinkedList;
import java.util.ListIterator;
/**
 * Simple timer class used to implement login and query timeouts.
 * <p/>
 * This thread runs as a Daemon thread to ensure that the java VM will exit
 * correctly when normal execution is complete.
 * <p/>
 * It provides both a singleton implementation and a default constructor for
 * the case when more than one timer thread is desired.
 *
 * @author Alin Sinpalean
 * @author Mike Hutchinson
 * @version $Id: TimerThread.java,v 1.5 2005/04/28 14:29:31 alin_sinpalean Exp $
 */
public class TimerThread extends Thread {
    /**
     * Interface to be implemented by classes that request timer services.
     */
    public interface TimerListener {
        /**
         * Event to be fired when the timeout expires.
         */
        void timerExpired();
    }
    /**
     * Internal class associating a login or query timeout value with a target
     * <code>TimerListener</code>.
     */
    private static class TimerRequest {
        /** The time when this timeout will expire. */
        final long time;
        /** Target to notify when the timeout expires. */
        final TimerListener target;
        /**
         * Create a <code>TimerRequest</code>.
         *
         * @param timeout the desired timeout in milliseconds
         * @param target  the target object; one of <code>SharedSocket</code> or
         *                <code>TdsCore</code>
         * @throws IllegalArgumentException if the timeout is negative or 0
         */
        TimerRequest(int timeout, TimerListener target) {
            if (timeout <= 0) {
                throw new IllegalArgumentException("Invalid timeout parameter "
                        + timeout);
            }
            this.time = System.currentTimeMillis() + (timeout);
            this.target = target;
        }
    }
    /** Singleton instance. */
    private static TimerThread instance;
    /** List of <code>TimerRequest</code>s to execute, ordered by time. */
    private final LinkedList timerList = new LinkedList();
    /** Time when the first request time out should occur. */
    private long nextTimeout;
    /**
     * Singleton getter.
     */
    public static synchronized TimerThread getInstance() {
        if (instance == null) {
            instance = new TimerThread();
            instance.start();
        }
        return instance;
    }
    /**
     * Construct a new <code>TimerThread</code> instance.
     */
    public TimerThread() {
        // Set the thread name
        super("jTDS TimerThread");
        // Ensure that this thread does not prevent the VM from exiting
        this.setDaemon(true);
    }
    /**
     * Execute the <code>TimerThread</code> main loop.
     */
    public void run() {
        synchronized (timerList) {
            while (true) {
                try {
                    try {
                        // If nextTimeout == 0 (i.e. there are no more requests
                        // in the queue) wait indefinitely -- wait(0)
                        timerList.wait(nextTimeout == 0 ? 0
                                : nextTimeout - System.currentTimeMillis());
                    } catch (IllegalArgumentException ex) {
                        // Timeout was negative, fire timeout
                    }
                    // Fire expired timeout requests
                    long time = System.currentTimeMillis();
                    while (!timerList.isEmpty()) {
                        // Examime the head of the list and see
                        // if the timer has expired.
                        TimerRequest t = (TimerRequest) timerList.getFirst();
                        if (t.time > time) {
                            break; // No timers have expired
                        }
                        // Notify target of timeout
                        t.target.timerExpired();
                        // Remove the fired timeout request
                        timerList.removeFirst();
                    }
                    // Determine next timeout
                    updateNextTimeout();
                } catch (InterruptedException e) {
                    // nop
                }
            }
        }
    }
    /**
     * Add a timer request to the queue.
     * <p/>
     * The queue is ordered by time so that the head of the list is always the
     * first timer to expire.
     *
     * @param timeout the interval in milliseconds after which the timer will
     *                expire
     * @param l       <code>TimerListener</code> to be notified on timeout
     * @return a handle to the timer request, that can later be used with
     *         <code>cancelTimer</code>
     */
    public Object setTimer(int timeout, TimerListener l) {
        // Create a new timer request
        TimerRequest t = new TimerRequest(timeout, l);
        synchronized (timerList) {
            if (timerList.isEmpty()) {
                // List was empty, just add new request
                timerList.add(t);
            } else {
                // Tiny optimization; new requests will usually go to the end
                TimerRequest crt = (TimerRequest) timerList.getLast();
                if (t.time >= crt.time) {
                    timerList.addLast(t);
                } else {
                    // Iterate the list and insert it into the right place
                    for (ListIterator li = timerList.listIterator(); li.hasNext(); ) {
                        crt = (TimerRequest) li.next();
                        if (t.time < crt.time) {
                            li.previous();
                            li.add(t);
                            break;
                        }
                    }
                }
            }
            // If this request is now the first in the list, interupt timer
            if (timerList.getFirst() == t) {
                nextTimeout = t.time;
                this.interrupt();
            }
        }
        // Return the created request as timer handle
        return t;
    }
    /**
     * Remove a redundant timer before it expires.
     *
     * @param handle handle to the request to be removed from the queue (a
     *        <code>TimerRequest</code> instance)
     * @return <code>true</code> if timer had not expired
     */
    public boolean cancelTimer(Object handle) {
        TimerRequest t = (TimerRequest) handle;
        synchronized (timerList) {
            boolean result = timerList.remove(t);
            if (nextTimeout == t.time) {
                updateNextTimeout();
            }
            return result;
        }
    }
    /**
     * Check whether a timer has expired.
     *
     * @param handle handle to the request to be checked for expiry (a
     *        <code>TimerRequest</code> instance)
     * @return <code>true</code> if timer has expired
     */
    public boolean hasExpired(Object handle) {
        TimerRequest t = (TimerRequest) handle;
        synchronized (timerList) {
            return !timerList.contains(t);
        }
    }
    /** Internal method that updates the value of {@link #nextTimeout}. */
    private void updateNextTimeout() {
        nextTimeout = timerList.isEmpty() ? 0
                : ((TimerRequest) timerList.getFirst()).time;
    }
}