package org.jacorb.util.threadpool; /* * JacORB - a free Java ORB * * Copyright (C) 1999-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ import java.util.LinkedList; import org.slf4j.Logger; import org.jacorb.config.Configuration; import org.omg.CORBA.NO_RESOURCES; /** * @author Nicolas Noffke */ public class ThreadPool { private final int max_threads; private final int max_idle_threads; private int total_threads = 0; private int idle_threads = 0; private final LinkedList job_queue; private final ConsumerFactory factory; private final String namePrefix; private int threadCount = 0; /** * <code>logger</code> is the logger for threadpool. */ private final Logger logger; /** * <code>shutdown</code> denotes whether to shutdown the pool. */ private boolean shutdown; public ThreadPool( Configuration configuration, String threadNamePrefix, ConsumerFactory factory, int max_threads, int max_idle_threads) { namePrefix = threadNamePrefix; this.job_queue = new LinkedList (); this.factory = factory; this.max_threads = max_threads; this.max_idle_threads = max_idle_threads; logger = configuration.getLogger("org.jacorb.util.tpool"); } protected synchronized Object getJob() { idle_threads++; /* * Check job queue is empty before having surplus idle threads exit, * otherwise (as was done previously) if a large number of jobs get * enqueued just after a large number of previously non-idle threads * complete their jobs, then all the newly created threads as well as * the newly non-idle threads will exit until max_idle_threads is * reached before serving up any jobs, and if there are more jobs than * max_idle_threads despite the fact that enough threads once existed to * handle the queued jobs, the excess jobs will be blocked until the * jobs taking up the max_idle_threads complete. * * Also, checking the idle_thread count every time the thread is * notified and the job queue is empty ensures that the surplus idle * threads get cleaned up more quickly, otherwise idle threads can only * be cleaned up after completing a job. */ while( (! shutdown) && job_queue.isEmpty() ) { /* * This tells the newly idle thread to exit, because * there are already too much idle threads. */ if (idle_threads > max_idle_threads) { if (logger.isDebugEnabled()) { logger.debug("[" + idle_threads + "/" + total_threads + "] Telling thread to exit (too many idle)"); } return getShutdownJob(); } try { if (logger.isDebugEnabled()) { logger.debug("[" + idle_threads + "/" + total_threads + "] job queue empty"); } wait(); } catch( InterruptedException e ) { // ignored } } //pool is to be shut down completely if (shutdown) { return getShutdownJob(); } idle_threads--; if (logger.isDebugEnabled()) { logger.debug("[" + idle_threads + "/" + total_threads + "] removed idle thread (job scheduled)"); } return job_queue.removeFirst(); } /** * the returned null will cause the ConsumerTie to exit. * also decrement thread counters. */ private Object getShutdownJob() { total_threads--; idle_threads--; return null; } public synchronized void putJob( Object job ) { job_queue.add(job); notifyAll(); /* * Create a new thread if there aren't enough idle threads * to handle all the jobs in the queue and we haven't reached * the max thread limit. This ensures that there are always * enough idle threads to handle all the jobs in the queue * and no jobs get stuck blocked waiting for a thread to become * idle while we are still below the max thread limit. */ if ((job_queue.size() > idle_threads) && (total_threads < max_threads)) { createNewThread(); } else if ( job_queue.size() > idle_threads ) { // more jobs than idle threads. however // thread limit reached. if (logger.isDebugEnabled()) { logger.debug ( "(Pool)[" + idle_threads + "/" + total_threads + "] no idle threads but maximum number of threads reached (" + max_threads + ")" ); } job_queue.remove(job); throw new NO_RESOURCES("(Pool)[" + idle_threads + "/" + total_threads + "] no idle threads but maximum number of threads reached (" + max_threads + ")"); } } private void createNewThread() { if (logger.isDebugEnabled()) { logger.debug("[" + idle_threads + "/" + total_threads + "] creating new thread" ); } Thread thread = new Thread( new ConsumerTie( this, factory.create() )); thread.setName(namePrefix + (threadCount++)); thread.setDaemon( true ); thread.start(); total_threads++; } /** * <code>getLogger</code> returns the threadpools logger. * * @return a <code>Logger</code> value */ Logger getLogger () { return logger; } /** * <code>shutdown</code> will shutdown the pool. */ public synchronized void shutdown() { if (logger.isDebugEnabled()) { logger.debug("[" + idle_threads + "/" + total_threads + "] shutting down pool" ); } shutdown = true; notifyAll(); } } // ThreadPool