Java tutorial
// ======================================================================== // $Id: ThreadPool.java,v 1.41 2005/08/13 00:01:28 gregwilkins Exp $ // Copyright 1999-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // 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. // ======================================================================== package net.lightbody.bmp.proxy.jetty.util; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; import java.io.Serializable; /* ------------------------------------------------------------ */ /** * A pool of threads. * <p> * Avoids the expense of thread creation by pooling threads after their run methods exit for reuse. * <p> * If the maximum pool size is reached, jobs wait for a free thread. * Idle threads timeout and terminate until the minimum number of threads are running. * <p> * This implementation uses the run(Object) method to place a job on a queue, which is read by the * getJob(timeout) method. Derived implementations may specialize getJob(timeout) to obtain jobs * from other sources without queing overheads. * * @version $Id: ThreadPool.java,v 1.41 2005/08/13 00:01:28 gregwilkins Exp $ * @author Juancarlo Aez <juancarlo@modelistica.com> * @author Greg Wilkins <gregw@mortbay.com> */ public class ThreadPool implements LifeCycle, Serializable { static Log log = LogFactory.getLog(ThreadPool.class); static private int __pool = 0; public static final String __DAEMON = "net.lightbody.bmp.proxy.jetty.util.ThreadPool.daemon"; public static final String __PRIORITY = "net.lightbody.bmp.proxy.jetty.util.ThreadPool.priority"; /* ------------------------------------------------------------------- */ private Pool _pool; private Object _join = ""; private transient boolean _started; /* ------------------------------------------------------------------- */ /* * Construct */ public ThreadPool() { String name = this.getClass().getName(); int ld = name.lastIndexOf('.'); if (ld >= 0) name = name.substring(ld + 1); synchronized (ThreadPool.class) { name += __pool++; } _pool = new Pool(); _pool.setPoolClass(ThreadPool.PoolThread.class); setName(name); } /* ------------------------------------------------------------ */ /** * @return The name of the ThreadPool. */ public String getName() { return _pool.getPoolName(); } /* ------------------------------------------------------------ */ /** * Set the Pool name. All ThreadPool instances with the same Pool name will share the same Pool * instance. Thus they will share the same max, min and available Threads. The field values of * the first ThreadPool to call setPoolName with a specific name are used for the named Pool. * Subsequent ThreadPools that join the name pool will loose their private values. * * @param name Name of the Pool instance this ThreadPool uses or null for an anonymous private * pool. */ public void setName(String name) { synchronized (Pool.class) { if (isStarted()) { if ((name == null && _pool.getPoolName() != null) || (name != null && !name.equals(_pool.getPoolName()))) throw new IllegalStateException("started"); return; } if (name == null) { if (_pool.getPoolName() != null) { _pool = new Pool(); _pool.setPoolName(getName()); } } else if (!name.equals(getName())) { Pool pool = Pool.getPool(name); if (pool == null) _pool.setPoolName(name); else _pool = pool; } } } /* ------------------------------------------------------------ */ /** * @deprecated use getName() */ public String getPoolName() { return getName(); } /* ------------------------------------------------------------ */ /** * @deprecated use setName(String) */ public void setPoolName(String name) { setName(name); } /* ------------------------------------------------------------ */ /** * Delegated to the named or anonymous Pool. */ public boolean isDaemon() { return _pool.getAttribute(__DAEMON) != null; } /* ------------------------------------------------------------ */ /** * Delegated to the named or anonymous Pool. */ public void setDaemon(boolean daemon) { _pool.setAttribute(__DAEMON, daemon ? "true" : null); } /* ------------------------------------------------------------ */ /** * Is the pool running jobs. * * @return True if start() has been called. */ public boolean isStarted() { return _started; } /* ------------------------------------------------------------ */ /** * Get the number of threads in the pool. Delegated to the named or anonymous Pool. * * @see #getIdleThreads * @return Number of threads */ public int getThreads() { return _pool.size(); } /* ------------------------------------------------------------ */ /** * Get the number of idle threads in the pool. Delegated to the named or anonymous Pool. * * @see #getThreads * @return Number of threads */ public int getIdleThreads() { return _pool.available(); } /* ------------------------------------------------------------ */ /** * Get the minimum number of threads. Delegated to the named or anonymous Pool. * * @see #setMinThreads * @return minimum number of threads. */ public int getMinThreads() { return _pool.getMinSize(); } /* ------------------------------------------------------------ */ /** * Set the minimum number of threads. Delegated to the named or anonymous Pool. * * @see #getMinThreads * @param minThreads minimum number of threads */ public void setMinThreads(int minThreads) { _pool.setMinSize(minThreads); } /* ------------------------------------------------------------ */ /** * Set the maximum number of threads. Delegated to the named or anonymous Pool. * * @see #setMaxThreads * @return maximum number of threads. */ public int getMaxThreads() { return _pool.getMaxSize(); } /* ------------------------------------------------------------ */ /** * Set the maximum number of threads. Delegated to the named or anonymous Pool. * * @see #getMaxThreads * @param maxThreads maximum number of threads. */ public void setMaxThreads(int maxThreads) { _pool.setMaxSize(maxThreads); } /* ------------------------------------------------------------ */ /** * Get the maximum thread idle time. Delegated to the named or anonymous Pool. * * @see #setMaxIdleTimeMs * @return Max idle time in ms. */ public int getMaxIdleTimeMs() { return _pool.getMaxIdleTimeMs(); } /* ------------------------------------------------------------ */ /** * Set the maximum thread idle time. Threads that are idle for longer than this period may be * stopped. Delegated to the named or anonymous Pool. * * @see #getMaxIdleTimeMs * @param maxIdleTimeMs Max idle time in ms. */ public void setMaxIdleTimeMs(int maxIdleTimeMs) { _pool.setMaxIdleTimeMs(maxIdleTimeMs); } /* ------------------------------------------------------------ */ /** * Get the priority of the pool threads. * * @return the priority of the pool threads. */ public int getThreadsPriority() { int priority = Thread.NORM_PRIORITY; Object o = _pool.getAttribute(__PRIORITY); if (o != null) { priority = ((Integer) o).intValue(); } return priority; } /* ------------------------------------------------------------ */ /** * Set the priority of the pool threads. * * @param priority the new thread priority. */ public void setThreadsPriority(int priority) { _pool.setAttribute(__PRIORITY, new Integer(priority)); } /* ------------------------------------------------------------ */ /** * Set Max Read Time. * * @deprecated maxIdleTime is used instead. */ public void setMaxStopTimeMs(int ms) { log.warn("setMaxStopTimeMs is deprecated. No longer required."); } /* ------------------------------------------------------------ */ /* * Start the ThreadPool. Construct the minimum number of threads. */ public void start() throws Exception { _started = true; _pool.start(); } /* ------------------------------------------------------------ */ /** * Stop the ThreadPool. New jobs are no longer accepted,idle threads are interrupted and * stopJob is called on active threads. The method then waits * min(getMaxStopTimeMs(),getMaxIdleTimeMs()), for all jobs to stop, at which time killJob is * called. */ public void stop() throws InterruptedException { _started = false; _pool.stop(); synchronized (_join) { _join.notifyAll(); } } /* ------------------------------------------------------------ */ public void join() { while (isStarted() && _pool != null) { synchronized (_join) { try { if (isStarted() && _pool != null) _join.wait(30000); } catch (Exception e) { LogSupport.ignore(log, e); } } } } /* ------------------------------------------------------------ */ public void shrink() throws InterruptedException { _pool.shrink(); } /* ------------------------------------------------------------ */ /** * Run job. Give a job to the pool. * * @param job If the job is derived from Runnable, the run method is called, otherwise it is * passed as the argument to the handle method. */ public void run(Object job) throws InterruptedException { if (job == null) return; try { PoolThread thread = (PoolThread) _pool.get(getMaxIdleTimeMs()); if (thread != null) thread.run(this, job); else { log.warn("No thread for " + job); stopJob(null, job); } } catch (InterruptedException e) { throw e; } catch (Exception e) { log.warn(LogSupport.EXCEPTION, e); } } /* ------------------------------------------------------------ */ /** * Handle a job. Called by the allocated thread to handle a job. If the job is a Runnable, it's * run method is called. Otherwise this method needs to be specialized by a derived class to * provide specific handling. * * @param job The job to execute. * @exception InterruptedException */ protected void handle(Object job) throws InterruptedException { if (job != null && job instanceof Runnable) ((Runnable) job).run(); else log.warn("Invalid job: " + job); } /* ------------------------------------------------------------ */ /** * Stop a Job. This method is called by the Pool if a job needs to be stopped. The default * implementation does nothing and should be extended by a derived thread pool class if special * action is required. * * @param thread The thread allocated to the job, or null if no thread allocated. * @param job The job object passed to run. */ protected void stopJob(Thread thread, Object job) { } /* ------------------------------------------------------------ */ /** * Pool Thread class. The PoolThread allows the threads job to be retrieved and active status * to be indicated. */ public static class PoolThread extends Thread implements Pool.PondLife { Pool _pool; ThreadPool _jobPool; Object _job; ThreadPool _runPool; Object _run; int _id; String _name; /* ------------------------------------------------------------ */ public void enterPool(Pool pool, int id) { synchronized (this) { _pool = pool; _id = id; _name = _pool.getPoolName() + "-" + id; this.setName(_name); this.setDaemon(pool.getAttribute(__DAEMON) != null); Object o = pool.getAttribute(__PRIORITY); if (o != null) { this.setPriority(((Integer) o).intValue()); } this.start(); } } /* ------------------------------------------------------------ */ public int getID() { return _id; } /* ------------------------------------------------------------ */ public void poolClosing() { synchronized (this) { _pool = null; if (_run == null) notify(); else interrupt(); } } /* ------------------------------------------------------------ */ public void leavePool() { synchronized (this) { _pool = null; if (_jobPool == null && _runPool == null) notify(); if (_job != null && _jobPool != null) { _jobPool.stopJob(this, _job); _job = null; _jobPool = null; } if (_run != null && _runPool != null) { _runPool.stopJob(this, _run); _run = null; _runPool = null; } } } /* ------------------------------------------------------------ */ public void run(ThreadPool pool, Object job) { synchronized (this) { _jobPool = pool; _job = job; notify(); } } /* ------------------------------------------------------------ */ /** * ThreadPool run. Loop getting jobs and handling them until idle or stopped. */ public void run() { Object run = null; ThreadPool runPool = null; while (_pool != null && _pool.isStarted()) { try { synchronized (this) { // Wait for a job. if (run == null && _pool != null && _pool.isStarted() && _job == null) wait(_pool.getMaxIdleTimeMs()); if (_job != null) { run = _run = _job; _job = null; runPool = _runPool = _jobPool; _jobPool = null; } } // handle outside of sync if (run != null && runPool != null) runPool.handle(run); else if (run == null && _pool != null) _pool.shrink(); } catch (InterruptedException e) { LogSupport.ignore(log, e); } finally { synchronized (this) { boolean got = run != null; run = _run = null; runPool = _runPool = null; try { if (got && _pool != null) _pool.put(this); } catch (InterruptedException e) { LogSupport.ignore(log, e); } } } } } public String toString() { return _name; } } }