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();
}
}
}
}
}