package com.limegroup.gnutella.util; import java.util.List; import java.util.LinkedList; import java.util.Collections; /** * A pool of Runnables that are processed asynchronously. */ public class DefaultThreadPool implements ThreadPool { /** * The list of items to be processed. */ private final List QUEUE = new LinkedList(); /** * The name of the threads to be created. */ private final String NAME; /** * Whether or not the constructed thread should be a managed thread or not. */ private final boolean MANAGED; /** * The maximum number of threads to allow running concurrently. */ private final int MAX_THREADS; /** * The current number of ACTIVE threads. */ private int _activeRunners; /** * Constructs a new DefaultThreadPool using ManagedThreads. * The pool will allow unlimited threads to be created for processing. */ public DefaultThreadPool(String name) { this(name, true); } /** * Constructs a new DefaultThreadPool using ManagedThreads. * The pool will allow maxThreads threads to be created for processing. */ public DefaultThreadPool(String name, int maxThreads) { this(name, true, maxThreads); } /** * Constructs a new DefaultThreadPool which will creates Threads of the given name. * If managed is true, uses a ManagedThread for processing. Otherwise uses * a normal thread. The pool will allow unlimited threads to be created for processing. */ public DefaultThreadPool(String name, boolean managed) { this(name, managed, Integer.MAX_VALUE); } /** * Constructs a new DefaultThreadPool which will creates Threads of the given name. * If managed is true, uses a ManagedThread for processing. Otherwise uses * a normal thread. The pool will allow maxThreads threads to be created for processing. */ public DefaultThreadPool(String name, boolean managed, int maxThreads) { NAME = name; MANAGED = managed; MAX_THREADS = maxThreads; } /** * Adds the specified runnable to be processed. */ public void invokeLater(Runnable r) { synchronized(this) { QUEUE.add(r); notifyAll(); } // release lock to allow inactive thread to acquire & loop. Thread.yield(); synchronized(this) { startRunnerIfPossibleAndNecessary(); } } /** * Starts a new runner if a new active thread is allowed to be created. */ private synchronized void startRunnerIfPossibleAndNecessary() { if(!QUEUE.isEmpty() && _activeRunners < MAX_THREADS) { Thread runner; if(MANAGED) runner = new ManagedThread(new Processor(), NAME); else runner = new Thread(new Processor(), NAME); runner.setDaemon(true); _activeRunners++; runner.start(); } } /** * Gets the next item to process. */ private synchronized Runnable next() { if(!QUEUE.isEmpty()) return (Runnable)QUEUE.remove(0); else return null; } /** * A runnable that processes the queue. */ private class Processor implements Runnable { public void run() { try { Runnable next = next(); while(true) { if(next != null) next.run(); // Ideally this would be in a finally clause -- but if it // is then we can potentially ignore exceptions that were // thrown. synchronized(DefaultThreadPool.this) { // If something was added before we grabbed the lock, // process those items immediately instead of waiting next = next(); if(next != null) continue; // Wait a little bit to see if something new is going // to come in, so we don't needlessly kill/recreate // threads. try { DefaultThreadPool.this.wait(5 * 1000); } catch(InterruptedException ignored) {} // If something was added and notified us, process it // instead of exiting. next = next(); if(next != null) continue; // Otherwise, exit else break; } } } finally { // We must restart a new runner if something was added. // It's highly unlikely that something was added between // the try of one synchronized block & the finally of another, // but it technically is possible. // We cannot loop here because we'd lose any exceptions // that may have been thrown. synchronized(DefaultThreadPool.this) { _activeRunners--; startRunnerIfPossibleAndNecessary(); } } } } }