/*******************************************************************************
$Source: /cvs/repositories/openii3/project/java/source/org/openeai/threadpool/ThreadPoolImpl.java,v $
$Revision: 1.5 $
*******************************************************************************/
/**********************************************************************
This file is part of the OpenEAI Application Foundation or
OpenEAI Message Object API created by Tod Jackson
(tod@openeai.org) and Steve Wheat (steve@openeai.org) at
the University of Illinois Urbana-Champaign.
Copyright (C) 2002 The OpenEAI Software Foundation
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
For specific licensing details and examples of how this software
can be used to build commercial integration software or to implement
integrations for your enterprise, visit http://www.OpenEai.org/licensing.
*/
package org.openeai.threadpool;
// Thread Pool implementation class.
import java.util.Vector;
import java.lang.IllegalArgumentException;
import java.lang.NumberFormatException;
import java.lang.InterruptedException;
import java.lang.Thread;
import java.lang.Runnable;
import org.openeai.config.ThreadPoolConfig;
public class ThreadPoolImpl implements ThreadPool {
// max number of threads that can be created
private int _maxThreads = -1;
// min number of threads that must always be present
private int _minThreads = -1;
// max idle time after which a thread may be killed
private int _maxIdleTime = -1;
// A list i.e. vector of pending jobs
private Vector _pendingJobs = new Vector();
// A list i.e. vector of all available threads
private Vector _availableThreads = new Vector();
// The debug flag
private boolean _debug = false;
private boolean m_shutdown = false;
private boolean m_checkBeforeProcessing = false;
// This class represents an element in the available threads vector.
private class ThreadElement {
// if true then the thread can process a new job
private boolean _idle;
// serves as the key
private Thread _thread;
public ThreadElement(Thread thread) {
_thread = thread;
_idle = true;
}
}
public boolean checkBeforeProcessing() {
return m_checkBeforeProcessing;
}
// Each thread in the pool is an instance of this class
private class PoolThread extends Thread {
private Object _lock;
// pass in the pool instance for synchronization.
public PoolThread(Object lock) {
_lock = lock;
}
// This is where all the action is...
public void run() {
Runnable job = null;
while (true) {
while (true) {
synchronized(_lock) {
// Keep processing jobs until none availble
if (_pendingJobs.size() == 0) {
if (_debug)
System.out.println("Idle Thread...");
int index = findMe();
if (index == -1)
return;
((ThreadElement)_availableThreads.get(index))._idle = true;
break;
}
// Remove the job from the pending list.
job = (Runnable)_pendingJobs.firstElement();
_pendingJobs.removeElementAt(0);
}
// run the job
job.run();
job = null;
}
try {
synchronized(this) {
// if no idle time specified, wait till notified.
if (_maxIdleTime == -1)
wait();
else
wait(_maxIdleTime);
}
}
catch (InterruptedException e) {
// Cleanup if interrupted
synchronized(_lock) {
if (_debug)
System.out.println("Interrupted...");
removeMe();
}
return;
}
// Just been notified or the wait timed out
synchronized(_lock) {
// If there are no jobs, that means we "idled" out.
if (_pendingJobs.size() == 0) {
if (_minThreads != -1 && _availableThreads.size() > _minThreads) {
if (_debug)
System.out.println("Thread timed out...");
removeMe();
return;
}
}
}
}
}
}
public ThreadPoolImpl(ThreadPoolConfig tConfig) throws NumberFormatException, IllegalArgumentException {
this(tConfig.getProperties());
}
public ThreadPoolImpl(java.util.Properties props) throws NumberFormatException, IllegalArgumentException {
if (props == null)
return;
setShutdown(false);
Object o = props.getProperty("maxThreads");
if (o != null) {
int n = java.lang.Integer.parseInt((String)o);
if (n < 1)
throw new IllegalArgumentException("maxThreads must be an integral value greater than 0");
_maxThreads = n;
}
o = props.getProperty("minThreads");
if (o != null) {
int n = java.lang.Integer.parseInt((String)o);
if (n < 0)
throw new IllegalArgumentException("minThreads must be an integral value greater than or equal to 0");
if (n > _maxThreads)
throw new IllegalArgumentException("minThreads cannot be greater than maxThreads");
_minThreads = n;
}
o = props.getProperty("maxIdleTime");
if (o != null) {
int n = java.lang.Integer.parseInt((String)o);
if (n < 1)
throw new IllegalArgumentException("maxIdleTime must be an integral value greater than 0");
_maxIdleTime = n;
}
o = props.getProperty("debug");
if (o != null) {
_debug = true;
}
m_checkBeforeProcessing = new Boolean(props.getProperty("checkBeforeProcessing","false")).booleanValue();
}
/**
* Sets a flag indicating that the ThreadPool is in a 'shutdown' status. No more jobs can be
* added to the pool when this happens.
*/
public void shutdown() {
setShutdown(true);
}
private void setShutdown(boolean sd) {
m_shutdown = sd;
}
private boolean isShutdown() {
return m_shutdown;
}
/**
* Adds a 'job' to the ThreadPool to be executed in a Thread.
*
* @param job java.lang.Runnable the job to be added to the ThreadPool
* @throws - ThreadPoolException if it has any problems adding the job to the ThreadPool.
**/
synchronized public void addJob(java.lang.Runnable job) throws ThreadPoolException {
if (isShutdown()) {
throw new ThreadPoolException("ThreadPool is shutdown, can't add any jobs.");
}
if (m_checkBeforeProcessing) {
if (getJobsInProgress() >= _maxThreads) {
throw new ThreadPoolException("All jobs are in progress, can't add any more at this time (" +
getJobsInProgress() + ") jobs.");
}
}
_pendingJobs.add(job);
int index = findFirstIdleThread();
if (index == -1) {
// All threads are busy
if (_maxThreads == -1 || _availableThreads.size() < _maxThreads) {
// We can create another thread...
if (_debug)
System.out.println("Creating a new Thread...");
ThreadElement e = new ThreadElement(new PoolThread(this));
e._idle = false;
e._thread.start();
_availableThreads.add(e);
return;
}
// We are not allowed to create any more threads
// So, just return.
// When one of the busy threads is done,
// it will check the pending queue and will see this job.
if (_debug)
System.out.println("Max Threads created and all threads in the pool are busy.");
}
else {
// There is at least one idle thread
if (_debug)
System.out.println("Using an existing thread...");
((ThreadElement)_availableThreads.get(index))._idle = false;
synchronized(((ThreadElement)_availableThreads.get(index))._thread) {
((ThreadElement)_availableThreads.get(index))._thread.notify();
}
}
}
synchronized public Stats getStats() {
Stats stats = new Stats();
stats.maxThreads = _maxThreads;
stats.minThreads = _minThreads;
stats.maxIdleTime = _maxIdleTime;
stats.pendingJobs = _pendingJobs.size();
stats.numThreads = _availableThreads.size();
stats.jobsInProgress =
_availableThreads.size() - findNumIdleThreads();
stats.check = checkBeforeProcessing();
return(stats);
}
synchronized public int getJobsInProgress() {
return _availableThreads.size() - findNumIdleThreads();
}
//*****************************************//
// Important...
// All private methods must always be
// called from a synchronized method!!
//*****************************************//
// Called by the thread pool to find the number of idle threads
private int findNumIdleThreads() {
int idleThreads = 0;
int size = _availableThreads.size();
for (int i=0; i<size; i++) {
if (((ThreadElement)_availableThreads.get(i))._idle)
idleThreads++;
}
return(idleThreads);
}
// Called by the thread pool to find the first idle thread
private int findFirstIdleThread() {
int size = _availableThreads.size();
for (int i=0; i<size; i++) {
if (((ThreadElement)_availableThreads.get(i))._idle)
return(i);
}
return(-1);
}
// Called by a pool thread to find itself in the vector of available threads.
private int findMe() {
int size = _availableThreads.size();
for (int i=0; i<size; i++) {
if (((ThreadElement)_availableThreads.get(i))._thread == Thread.currentThread())
return(i);
}
return(-1);
}
// Called by a pool thread to remove itself from the vector of available threads
private void removeMe() {
int size = _availableThreads.size();
for (int i=0; i<size; i++) {
if (((ThreadElement)_availableThreads.get(i))._thread == Thread.currentThread()) {
_availableThreads.remove(i);
return;
}
}
}
}