Java tutorial
/* * Copyright 2004-2005 OpenSymphony * * Licensed 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. * */ /* * Previously Copyright (c) 2001-2004 James House */ package com.icbc.Scheduler.ThreadManager; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.icbc.Scheduler.Core.SchedulerEnviornment; import com.icbc.Scheduler.Global.GlobalDefine; import com.icbc.Scheduler.Job.Job; import com.icbc.Scheduler.Log.LogProvider; //import org.quartz.SchedulerConfigException; //import org.quartz.spi.ThreadPool; /** * <p> * This is class is a simple implementation of a thread pool, based on the * <code>{@link org.quartz.spi.ThreadPool}</code> interface. * </p> * * <p> * <CODE>Runnable</CODE> objects are sent to the pool with the <code>{@link #runInThread(Runnable)}</code> * method, which blocks until a <code>Thread</code> becomes available. * </p> * * <p> * The pool has a fixed number of <code>Thread</code>s, and does not grow or * shrink based on demand. * </p> * * @author James House * @author Juergen Donnerstag */ public class SchedulerThreadPool implements ThreadPool { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Data members. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private int count = -1; private int prio = Thread.NORM_PRIORITY; private boolean isShutdown = false; private boolean inheritLoader = false; private boolean inheritGroup = true; private boolean makeThreadsDaemons = false; private ThreadGroup threadGroup; // private Runnable nextRunnable; // private Object nextRunnableLock = new Object(); //201112 private SchedulerThreadQueue runnableQueue; private Object runnableQueueLock = new Object(); private int ThreadQueueSize = GlobalDefine.SchedulerThreadQueueSize; private WorkerThread[] workers; private String threadNamePrefix = "W";//Worker private String threadPoolName = ""; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Constructors. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * <p> * Create a new (unconfigured) <code>SchedulerThreadPool</code>. * </p> * * @see #setThreadCount(int) * @see #setThreadPriority(int) */ public SchedulerThreadPool(String p_threadPoolName) { this.threadPoolName = p_threadPoolName; } /** * <p> * Create a new <code>SchedulerThreadPool</code> with the specified number * of <code>Thread</code> s that have the given priority. * </p> * * @param threadCount * the number of worker <code>Threads</code> in the pool, must * be > 0. * @param threadPriority * the thread priority for the worker threads. * * @see java.lang.Thread */ public SchedulerThreadPool(int threadCount, int threadPriority) { setThreadCount(threadCount); setThreadPriority(threadPriority); } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Interface. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ public Log getLog() { return LogFactory.getLog(SchedulerThreadPool.class); } public int getPoolSize() { return getThreadCount(); } /*DEBUG USAGE*/ public String getPoolStatus() { String t_sPoolStatus = "TP[" + threadPoolName + "]:"; int t_iActiveCount = 0; int t_iNonActiveCount = 0; try { for (int i = 0; i < workers.length; i++) { if ((workers[i]).runnable == null) { t_iNonActiveCount++; } else { t_iActiveCount++; } } /* t_sPoolStatus = t_sPoolStatus + "ActiveThreadCount[" + t_iActiveCount+"]," +"NonavtiveCount[" + t_iNonActiveCount + "]";*/ t_sPoolStatus = t_sPoolStatus + "T[" + workers.length + "];AT[" + t_iActiveCount + "];NA[" + t_iNonActiveCount + "];Q[" + runnableQueue.getQueueSize() + "]"; } catch (Exception e) { t_sPoolStatus = "ERROR"; return t_sPoolStatus; } return t_sPoolStatus; } /** * <p> * Set the number of worker threads in the pool - has no effect after * <code>initialize()</code> has been called. * </p> */ public void setThreadCount(int count) { this.count = count; } /** * <p> * Get the number of worker threads in the pool. * </p> */ public int getThreadCount() { return count; } /** * <p> * Set the thread priority of worker threads in the pool - has no effect * after <code>initialize()</code> has been called. * </p> */ public void setThreadPriority(int prio) { this.prio = prio; } /** * <p> * Get the thread priority of worker threads in the pool. * </p> */ public int getThreadPriority() { return prio; } public void setThreadNamePrefix(String prfx) { this.threadNamePrefix = prfx; } public String getThreadNamePrefix() { return threadNamePrefix; } /** * @return Returns the * threadsInheritContextClassLoaderOfInitializingThread. */ public boolean isThreadsInheritContextClassLoaderOfInitializingThread() { return inheritLoader; } /** * @param inheritLoader * The threadsInheritContextClassLoaderOfInitializingThread to * set. */ public void setThreadsInheritContextClassLoaderOfInitializingThread(boolean inheritLoader) { this.inheritLoader = inheritLoader; } public boolean isThreadsInheritGroupOfInitializingThread() { return inheritGroup; } public void setThreadsInheritGroupOfInitializingThread(boolean inheritGroup) { this.inheritGroup = inheritGroup; } /** * @return Returns the value of makeThreadsDaemons. */ public boolean isMakeThreadsDaemons() { return makeThreadsDaemons; } /** * @param makeThreadsDaemons * The value of makeThreadsDaemons to set. */ public void setMakeThreadsDaemons(boolean makeThreadsDaemons) { this.makeThreadsDaemons = makeThreadsDaemons; } public void initialize() { runnableQueue = new SchedulerThreadQueue(ThreadQueueSize); if (count <= 0) { System.err.println("Thread count must be > 0"); } if (prio <= 0 || prio > 9) { System.err.println("Thread priority must be > 0 and <= 9"); } if (isThreadsInheritGroupOfInitializingThread()) { threadGroup = Thread.currentThread().getThreadGroup(); } else { // follow the threadGroup tree to the root thread group. threadGroup = Thread.currentThread().getThreadGroup(); ThreadGroup parent = threadGroup; while (!parent.getName().equals("main")) { threadGroup = parent; parent = threadGroup.getParent(); } threadGroup = new ThreadGroup(parent, "SchedulerThreadPool"); } if (isThreadsInheritContextClassLoaderOfInitializingThread()) { getLog().info( "Job execution threads will use class loader of thread: " + Thread.currentThread().getName()); } // create the worker threads and start them workers = createWorkerThreads(count); for (int i = 0; i < count; ++i) { if (isThreadsInheritContextClassLoaderOfInitializingThread()) { workers[i].setContextClassLoader(Thread.currentThread().getContextClassLoader()); } } LogProvider.outLog("INFO", threadPoolName + " initialize successfully!", GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); } protected WorkerThread[] createWorkerThreads(int count) { workers = new WorkerThread[count]; for (int i = 0; i < count; ++i) { if (this == null) { LogProvider.outLog("ERROR", "createWorkerThreads-SchedulerThreadPool IS NULL", GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); } if (GlobalDefine.THREAD_NAME_APP.equals(threadPoolName) || GlobalDefine.THREAD_NAME_MODULE.equals(threadPoolName) || GlobalDefine.THREAD_NAME_SYSTEM.equals(threadPoolName) || GlobalDefine.THREAD_NAME_OTHER.equals(threadPoolName)) { workers[i] = new WorkerThread(this, threadGroup, threadPoolName.substring(0, 1) + getThreadNamePrefix() + "-" + i, getThreadPriority(), isMakeThreadsDaemons()); } else { workers[i] = new WorkerThread(this, threadGroup, threadPoolName + getThreadNamePrefix() + "-" + i, getThreadPriority(), isMakeThreadsDaemons()); } } return workers; } /** * <p> * Terminate any worker threads in this thread group. * </p> * * <p> * Jobs currently in progress will complete. * </p> */ public void shutdown() { shutdown(true); } /** * <p> * Terminate any worker threads in this thread group. * </p> * * <p> * Jobs currently in progress will complete. * </p> */ public void shutdown(boolean waitForJobsToComplete) { isShutdown = true; // signal each worker thread to shut down for (int i = 0; i < workers.length; i++) { if (workers[i] != null) workers[i].shutdown(); } /* // Give waiting (wait(1000)) worker threads a chance to shut down. // Active worker threads will shut down after finishing their // current job. synchronized (nextRunnableLock) { nextRunnableLock.notifyAll(); } */ synchronized (runnableQueueLock) { runnableQueueLock.notifyAll(); } if (waitForJobsToComplete == true) { // Wait until all worker threads are shut down int alive = workers.length; while (alive > 0) { alive = 0; for (int i = 0; i < workers.length; i++) { if (workers[i].isAlive()) { try { //if (logger.isDebugEnabled()) getLog().debug("Waiting for thread no. " + i + " to shut down"); // note: with waiting infinite - join(0) - the // application // may appear to 'hang'. Waiting for a finite time // however // requires an additional loop (alive). alive++; workers[i].join(200); } catch (InterruptedException ex) { } } } } //if (logger.isDebugEnabled()) { int activeCount = threadGroup.activeCount(); if (activeCount > 0) getLog().info("There are still " + activeCount + " worker threads active." + " See javadoc runInThread(Runnable) for a possible explanation"); getLog().debug("shutdown complete"); //} } } /** * <p> * Run the given <code>Runnable</code> object in the next available * <code>Thread</code>. If while waiting the thread pool is asked to * shut down, the Runnable is executed immediately within a new additional * thread. * </p> * * @param runnable * the <code>Runnable</code> to be added. */ public boolean runInThread(Runnable runnable) { if (runnable == null) return false; /*DEBUG USAGE*/ /* LogProvider.outLog("INFO", "Before runInThread:" + ((Job)runnable).getJobName() + GlobalDefine.RETURNCHAR + "ThreadStatus:" + getPoolStatus(), GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, SchedulerThreadPool.class.getName()); *//* LogProvider.outLog("INFO", "runInThread:" + ((Job)runnable).getJobName() + "; " + getPoolStatus(), GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, SchedulerThreadPool.class.getName()); */ if (isShutdown) { try { getLog().info( "SchedulerThreadPool.runInThread(): thread pool has been shutdown. Runnable will not be executed"); } catch (Exception e) { // ignore to help with a tomcat glitch } return false; } /* synchronized (nextRunnableLock) { // Wait until a worker thread has taken the previous Runnable // or until the thread pool is asked to shutdown. while ((nextRunnable != null) && !isShutdown) { try { nextRunnableLock.wait(1000); } catch (InterruptedException ignore) { } } // During normal operation, not shutdown, set the nextRunnable // and notify the worker threads waiting (getNextRunnable()). if (!isShutdown) { nextRunnable = runnable; nextRunnableLock.notifyAll(); } } */ synchronized (runnableQueueLock) { while (!runnableQueue.push(runnable) && !isShutdown) { try { LogProvider.outLog("WARN", threadPoolName + " is full! Wait " + GlobalDefine.SchedulerThreadQueueFullWait + "ms", GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); runnableQueueLock.wait(GlobalDefine.SchedulerThreadQueueFullWait); } catch (InterruptedException ignore) { } } } // If the thread pool is going down, execute the Runnable // within a new additional worker thread (no thread from the pool). // note: the synchronized section should be as short (time) as // possible. Starting a new thread is not a quick action. if (isShutdown) { if (this == null) { LogProvider.outLog("ERROR", "runInThread-SchedulerThreadPool IS NULL", GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); } new WorkerThread(this, threadGroup, "WorkerThread-LastJob", prio, false, runnable); } return true; } /** * <p> * Dequeue the next pending <code>Runnable</code>. * </p> * * <p> * getNextRunnable() should return null if within a specific time no new * Runnable is available. This gives the worker thread the chance to check * its shutdown flag. In case the worker thread is asked to shut down it * will notify on nextRunnableLock, hence interrupt the wait state. That * is, the time used for waiting need not be short. * </p> */ private Runnable getNextRunnable() throws InterruptedException { Runnable toRun = null; /* if (nextRunnableLock == null){ LogProvider.outLog("ERROR", "ERROR IN getNextRunnable:nextRunnableLock is null", GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); return toRun; } */ if (runnableQueueLock == null) { LogProvider.outLog("ERROR", "ERROR IN getNextRunnable:runnableQueueLock is null", GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); return toRun; } if (runnableQueue == null) { LogProvider.outLog("ERROR", "runnableQueue is null!!!!", GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); return toRun; } /* // Wait for new Runnable (see runInThread()) and notify runInThread() // in case the next Runnable is already waiting. synchronized (nextRunnableLock) { try{ if (nextRunnable == null) nextRunnableLock.wait(1000); if (nextRunnable != null) { toRun = nextRunnable; nextRunnable = null; nextRunnableLock.notifyAll(); } }catch(Exception e){ LogProvider.outException(e, "ERROR IN getNextRunnable ", "WorkerThread_run"); throw new InterruptedException(); } } */ synchronized (runnableQueueLock) { if (!runnableQueue.isEmpty()) { toRun = (Runnable) runnableQueue.pop(); } } return toRun; } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * WorkerThread Class. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * <p> * A Worker loops, waiting to execute tasks. * </p> */ class WorkerThread extends Thread { // A flag that signals the WorkerThread to terminate. private boolean run = true; private SchedulerThreadPool tp; //private Runnable runnable = null; /*DEBUG USAGE*/ public Runnable runnable = null; /** * <p> * Create a worker thread and start it. Waiting for the next Runnable, * executing it, and waiting for the next Runnable, until the shutdown * flag is set. * </p> */ WorkerThread(SchedulerThreadPool tp, ThreadGroup threadGroup, String name, int prio, boolean isDaemon) { this(tp, threadGroup, name, prio, isDaemon, null); } /** * <p> * Create a worker thread, start it, execute the runnable and terminate * the thread (one time execution). * </p> */ WorkerThread(SchedulerThreadPool tp, ThreadGroup threadGroup, String name, int prio, boolean isDaemon, Runnable runnable) { super(threadGroup, name); if (tp == null) { LogProvider.outLog("ERROR", "WorkerThread-TP IS NULL:" + name, GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); } this.tp = tp; this.runnable = runnable; setPriority(prio); setDaemon(isDaemon); start(); } /** * <p> * Signal the thread that it should terminate. * </p> */ void shutdown() { run = false; // @todo I'm not really sure if we should interrupt the thread. // Javadoc mentions that it interrupts blocked I/O operations as // well. Hence the job will most likely fail. I think we should // shut the work thread gracefully, by letting the job finish // uninterrupted. See SchedulerThreadPool.shutdown() //interrupt(); } /** * <p> * Loop, executing targets as they are received. * </p> */ public void run() { boolean runOnce = (runnable != null); while (run) { if (tp == null) { LogProvider.outLog("ERROR", "ERROR IN Thread_run:tp is null", GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); return; } try { if (runnable == null) { runnable = tp.getNextRunnable(); } if (runnable != null) { runnable.run(); } } catch (InterruptedException unblock) { // do nothing (loop will terminate if shutdown() was called try { getLog().error("worker threat got interrupted", unblock); LogProvider.outException(unblock, "ERROR IN InterruptedException ", "WorkerThread_run"); } catch (Exception e) { // ignore to help with a tomcat glitch } } catch (Exception exceptionInRunnable) { try { getLog().error("Error while executing the Runnable: ", exceptionInRunnable); LogProvider.outException(exceptionInRunnable, "ERROR IN Thread_run ", "WorkerThread_run"); } catch (Exception e) { // ignore to help with a tomcat glitch } } finally { if (runOnce) run = false; runnable = null; // repair the thread in case the runnable mucked it up... setPriority(tp.getThreadPriority()); } try { sleep(GlobalDefine.SchedulerThreadWorkerSleepTime); } catch (InterruptedException e) { LogProvider.outLog("ERROR", "ERROR IN Thread sleep " + e.getStackTrace(), GlobalDefine.LOG_LOGGER_DYNAMIC_FILE, "WorkerThread_run"); } } //if (log.isDebugEnabled()) try { getLog().debug("WorkerThread is shutting down"); } catch (Exception e) { // ignore to help with a tomcat glitch } } } }