FileDocCategorySizeDatePackage
ThreadPool.javaAPI DocApache Tomcat 6.0.1426103Fri Jul 20 04:20:36 BST 2007org.apache.tomcat.util.threads

ThreadPool

public class ThreadPool extends Object
A thread pool that is trying to copy the apache process management. Should we remove this in favor of Doug Lea's thread package?
author
Gal Shachor
author
Yoav Shapira

Fields Summary
private static org.apache.juli.logging.Log
log
private static org.apache.tomcat.util.res.StringManager
sm
private static boolean
logfull
public static final int
MAX_THREADS
public static final int
MAX_THREADS_MIN
public static final int
MAX_SPARE_THREADS
public static final int
MIN_SPARE_THREADS
public static final int
WORK_WAIT_TIMEOUT
protected ControlRunnable[]
pool
protected MonitorRunnable
monitor
protected int
maxThreads
protected int
minSpareThreads
protected int
maxSpareThreads
protected int
currentThreadCount
protected int
currentThreadsBusy
protected boolean
stopThePool
protected boolean
isDaemon
protected Hashtable
threads
The threads that are part of the pool. Key is Thread, value is the ControlRunnable
protected Vector
listeners
protected String
name
Name of the threadpool
protected int
sequence
Sequence.
protected int
threadPriority
Thread priority.
Constructors Summary
public ThreadPool()
Constructor.



              
      
        maxThreads = MAX_THREADS;
        maxSpareThreads = MAX_SPARE_THREADS;
        minSpareThreads = MIN_SPARE_THREADS;
        currentThreadCount = 0;
        currentThreadsBusy = 0;
        stopThePool = false;
    
Methods Summary
public voidaddThread(java.lang.Thread t, org.apache.tomcat.util.threads.ThreadPool$ControlRunnable cr)

        threads.put( t, cr );
        for( int i=0; i<listeners.size(); i++ ) {
            ThreadPoolListener tpl=(ThreadPoolListener)listeners.elementAt(i);
            tpl.threadStart(this, t);
        }
    
public voidaddThreadPoolListener(org.apache.tomcat.util.threads.ThreadPool$ThreadPoolListener tpl)

        listeners.addElement( tpl );
    
protected voidadjustLimits()

        if(maxThreads <= 0) {
            maxThreads = MAX_THREADS;
        } else if (maxThreads < MAX_THREADS_MIN) {
            log.warn(sm.getString("threadpool.max_threads_too_low",
                                  new Integer(maxThreads),
                                  new Integer(MAX_THREADS_MIN)));
            maxThreads = MAX_THREADS_MIN;
        }

        if(maxSpareThreads >= maxThreads) {
            maxSpareThreads = maxThreads;
        }

        if(maxSpareThreads <= 0) {
            if(1 == maxThreads) {
                maxSpareThreads = 1;
            } else {
                maxSpareThreads = maxThreads/2;
            }
        }

        if(minSpareThreads >  maxSpareThreads) {
            minSpareThreads =  maxSpareThreads;
        }

        if(minSpareThreads <= 0) {
            if(1 == maxSpareThreads) {
                minSpareThreads = 1;
            } else {
                minSpareThreads = maxSpareThreads/2;
            }
        }
    
protected synchronized voidcheckSpareControllers()
Called by the monitor thread to harvest idle threads.


        if(stopThePool) {
            return;
        }

        if((currentThreadCount - currentThreadsBusy) > maxSpareThreads) {
            int toFree = currentThreadCount -
                         currentThreadsBusy -
                         maxSpareThreads;

            for(int i = 0 ; i < toFree ; i++) {
                ControlRunnable c = pool[currentThreadCount - currentThreadsBusy - 1];
                c.terminate();
                pool[currentThreadCount - currentThreadsBusy - 1] = null;
                currentThreadCount --;
            }

        }

    
public static org.apache.tomcat.util.threads.ThreadPoolcreateThreadPool(boolean jmx)
Create a ThreadPool instance.

param
jmx UNUSED
return
ThreadPool instance. If JMX support is requested, you need to call register() in order to set a name.

        return new ThreadPool();
    
private org.apache.tomcat.util.threads.ThreadPool$ControlRunnablefindControlRunnable()

        ControlRunnable c=null;

        if ( stopThePool ) {
            throw new IllegalStateException();
        }

        // Obtain a free thread from the pool.
        synchronized(this) {

            while (currentThreadsBusy == currentThreadCount) {
                 // All threads are busy
                if (currentThreadCount < maxThreads) {
                    // Not all threads were open,
                    // Open new threads up to the max number of idel threads
                    int toOpen = currentThreadCount + minSpareThreads;
                    openThreads(toOpen);
                } else {
                    logFull(log, currentThreadCount, maxThreads);
                    // Wait for a thread to become idel.
                    try {
                        this.wait();
                    }
                    // was just catch Throwable -- but no other
                    // exceptions can be thrown by wait, right?
                    // So we catch and ignore this one, since
                    // it'll never actually happen, since nowhere
                    // do we say pool.interrupt().
                    catch(InterruptedException e) {
                        log.error("Unexpected exception", e);
                    }
		    if( log.isDebugEnabled() ) {
			log.debug("Finished waiting: CTC="+currentThreadCount +
				  ", CTB=" + currentThreadsBusy);
                    }
                    // Pool was stopped. Get away of the pool.
                    if( stopThePool) {
                        break;
                    }
                }
            }
            // Pool was stopped. Get away of the pool.
            if(0 == currentThreadCount || stopThePool) {
                throw new IllegalStateException();
            }
                    
            // If we are here it means that there is a free thread. Take it.
            int pos = currentThreadCount - currentThreadsBusy - 1;
            c = pool[pos];
            pool[pos] = null;
            currentThreadsBusy++;

        }
        return c;
    
public intgetCurrentThreadCount()

        return currentThreadCount;
    
public intgetCurrentThreadsBusy()

        return currentThreadsBusy;
    
public booleangetDaemon()

        return isDaemon;
    
public static intgetDebug()

        return 0;
    
public intgetMaxSpareThreads()

        return maxSpareThreads;
    
public intgetMaxThreads()

        return maxThreads;
    
public intgetMinSpareThreads()

        return minSpareThreads;
    
public org.apache.tomcat.util.threads.ThreadPool$MonitorRunnablegetMonitor()

        return monitor;
    
public java.lang.StringgetName()

        return name;
    
public intgetSequence()

        return sequence;
    
public java.lang.String[]getThreadParam()
Return an array with the current "param" ( XXX better name ? ) of each thread. This is typically the last request.

return
The params of all threads

        String status[]=new String[ threads.size()];
        Iterator it=threads.keySet().iterator();
        for( int i=0; ( i<status.length && it.hasNext()); i++ ) {
            ThreadWithAttributes twa=(ThreadWithAttributes)
                    it.next();
            Object o=twa.getParam(this);
            status[i]=(o==null)? null : o.toString();
        }
        return status;
    
public intgetThreadPriority()
Returns the priority level of current and future threads in this pool.

return
The priority

      return threadPriority;
    
public java.lang.String[]getThreadStatus()
Return an array with the status of each thread. The status indicates the current request processing stage ( for tomcat ) or whatever the thread is doing ( if the application using TP provide this info )

return
The status of all threads

        String status[]=new String[ threads.size()];
        Iterator it=threads.keySet().iterator();
        for( int i=0; ( i<status.length && it.hasNext()); i++ ) {
            ThreadWithAttributes twa=(ThreadWithAttributes)
                    it.next();
            status[i]=twa.getCurrentStage(this);
        }
        return status;
    
public java.util.EnumerationgetThreads()

        return threads.keys();
    
public intincSequence()

        return sequence++;
    
public booleanisDaemon()

        return isDaemon;
    
voidlog(java.lang.String s)

deprecated

	log.info(s);
	//loghelper.flush();
    
private static voidlogFull(org.apache.juli.logging.Log loghelper, int currentThreadCount, int maxThreads)

	if( logfull ) {
            log.error(sm.getString("threadpool.busy",
                                   new Integer(currentThreadCount),
                                   new Integer(maxThreads)));
            logfull=false;
        } else if( log.isDebugEnabled() ) {
            log.debug("All threads are busy " + currentThreadCount + " " +
                      maxThreads );
        }
    
protected synchronized voidnotifyThreadEnd(org.apache.tomcat.util.threads.ThreadPool$ControlRunnable c)
Inform the pool that the specific thread finish. Called by the ControlRunnable.run() when the runnable throws an exception.

        currentThreadsBusy--;
        currentThreadCount --;
        notify();
    
protected voidopenThreads(int toOpen)
Create missing threads.

param
toOpen Total number of threads we'll have open


        if(toOpen > maxThreads) {
            toOpen = maxThreads;
        }

        for(int i = currentThreadCount ; i < toOpen ; i++) {
            pool[i - currentThreadsBusy] = new ControlRunnable(this);
        }

        currentThreadCount = toOpen;
    
public voidremoveThread(java.lang.Thread t)

        threads.remove(t);
        for( int i=0; i<listeners.size(); i++ ) {
            ThreadPoolListener tpl=(ThreadPoolListener)listeners.elementAt(i);
            tpl.threadEnd(this, t);
        }
    
protected synchronized voidreturnController(org.apache.tomcat.util.threads.ThreadPool$ControlRunnable c)
Returns the thread to the pool. Called by threads as they are becoming idel.


        if(0 == currentThreadCount || stopThePool) {
            c.terminate();
            return;
        }

        // atomic
        currentThreadsBusy--;

        pool[currentThreadCount - currentThreadsBusy - 1] = c;
        notify();
    
public voidrun(java.lang.Runnable r)

        ControlRunnable c = findControlRunnable();
        c.runIt(r);
    
public voidrunIt(ThreadPoolRunnable r)
Executes a given Runnable on a thread in the pool, block if needed.

        if(null == r) {
            throw new NullPointerException();
        }

        ControlRunnable c = findControlRunnable();
        c.runIt(r);
    
public voidsetDaemon(boolean b)
The default is true - the created threads will be in daemon mode. If set to false, the control thread will not be daemon - and will keep the process alive.

        isDaemon=b;
    
public voidsetMaxSpareThreads(int maxSpareThreads)

        this.maxSpareThreads = maxSpareThreads;
    
public voidsetMaxThreads(int maxThreads)

        this.maxThreads = maxThreads;
    
public voidsetMinSpareThreads(int minSpareThreads)

        this.minSpareThreads = minSpareThreads;
    
public voidsetName(java.lang.String name)

        this.name = name;
    
public synchronized voidsetThreadPriority(int threadPriority)
Sets the thread priority for current and future threads in this pool.

param
threadPriority The new priority
throws
IllegalArgumentException If the specified priority is less than Thread.MIN_PRIORITY or more than Thread.MAX_PRIORITY

        if(log.isDebugEnabled())
            log.debug(getClass().getName() +
                      ": setPriority(" + threadPriority + "): here.");

      if (threadPriority < Thread.MIN_PRIORITY) {
        throw new IllegalArgumentException("new priority < MIN_PRIORITY");
      } else if (threadPriority > Thread.MAX_PRIORITY) {
        throw new IllegalArgumentException("new priority > MAX_PRIORITY");
      }

      // Set for future threads
      this.threadPriority = threadPriority;

      Enumeration currentThreads = getThreads();
      Thread t = null;
      while(currentThreads.hasMoreElements()) {
        t = (Thread) currentThreads.nextElement();
        t.setPriority(threadPriority);
      } 
    
public synchronized voidshutdown()
Stop the thread pool

        if(!stopThePool) {
            stopThePool = true;
            if (monitor != null) {
                monitor.terminate();
                monitor = null;
            }
            for(int i = 0; i < currentThreadCount - currentThreadsBusy; i++) {
                try {
                    pool[i].terminate();
                } catch(Throwable t) {
                    /*
		     * Do nothing... The show must go on, we are shutting
		     * down the pool and nothing should stop that.
		     */
		    log.error("Ignored exception while shutting down thread pool", t);
                }
            }
            currentThreadsBusy = currentThreadCount = 0;
            pool = null;
            notifyAll();
        }
    
public synchronized voidstart()

	stopThePool=false;
        currentThreadCount  = 0;
        currentThreadsBusy  = 0;

        adjustLimits();

        pool = new ControlRunnable[maxThreads];

        openThreads(minSpareThreads);
        if (maxSpareThreads < maxThreads) {
            monitor = new MonitorRunnable(this);
        }
    
public java.lang.StringthreadStatusString()
Debug display of the stage of each thread. The return is html style, for display in the console ( it can be easily parsed too ).

return
The thread status display

        StringBuffer sb=new StringBuffer();
        Iterator it=threads.keySet().iterator();
        sb.append("<ul>");
        while( it.hasNext()) {
            sb.append("<li>");
            ThreadWithAttributes twa=(ThreadWithAttributes)
                    it.next();
            sb.append(twa.getCurrentStage(this) ).append(" ");
            sb.append( twa.getParam(this));
            sb.append( "</li>\n");
        }
        sb.append("</ul>");
        return sb.toString();