/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
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., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package visad.util;
import java.util.Collection;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
/**
* A pool of threads which can be used to execute any Runnable tasks. Internally
* this class uses a java.util.concurrent.ThreadPoolExecutor, but originally it
* did not because the original class predates java.util.concurrent. As the
* internals of this class have evolved, an effort has been made to preserve the
* original API. Note that a java.util.concurrent.ThreadPoolExecutor does not
* support the notion of minimum and maximum threads so minimum threads is
* ignored, and maximum threads is simply the size of the thread pool.
*/
public class ThreadPool {
/** Default prefix */
private static final String DEFAULT_PREFIX = ThreadPool.class.toString();
/** Thread pool from core Java */
private final ThreadPoolExecutor exec;
/**
* We just need a thread-safe, lock-free, high-performance bag. It does not
* matter that it is a queue.
*/
private final Collection<Future<?>> bagOfFutures = new ConcurrentLinkedQueue<Future<?>>();
/** waitForTasks requires that we lock this class for a moment */
private final Object mutex = new Object();
/**
* The available processors on the system. Java Concurrency in Practice, 8.2
* has more information on tuning this number
*/
private static final int PROCESSORS = Runtime.getRuntime()
.availableProcessors() + 1;
/** The prefix for this thread pool. */
private final String prefix;
/**
* Build a thread pool with the default thread name prefix and the default
* minimum and maximum numbers of threads.
*
* @throws Exception
*/
public ThreadPool() throws Exception {
this(DEFAULT_PREFIX, 0, PROCESSORS);
}
/**
* Build a thread pool with the specified thread name prefix, and the default
* minimum and maximum numbers of threads
*
* @param prefix
*
* @throws Exception
*/
public ThreadPool(String prefix) throws Exception {
this(prefix, 0, PROCESSORS);
}
/**
* Build a thread pool with the specified maximum number of threads, and the
* default thread name prefix and minimum number of threads
*
* @param max
*
* @throws Exception
*/
public ThreadPool(int max) throws Exception {
this(DEFAULT_PREFIX, 0, max);
}
/**
* Build a thread pool with the specified minimum and maximum numbers of
* threads, and the default thread name prefix.
*
* @param min
* @param max
*
* @throws Exception
*/
public ThreadPool(int min, int max) throws Exception {
this(DEFAULT_PREFIX, min, max);
}
/**
* Build a thread pool with the specified thread name prefix and minimum and
* maximum numbers of threads
*
* @param prefix
* @param min
* @param max
*
* @throws Exception
*/
public ThreadPool(String prefix, int min, int max) throws Exception {
this.prefix = prefix;
// Tom & Don TODO:
// I would say it is never advisable to go below PROCESSORS constant. The
// test I ran assume this. But I also want to keep the API as it stands now.
// I'll let you make the final call here.
// Could check for this: exec = (ThreadPoolExecutor) Executors.newFixedThreadPool(max < PROCESSORS ? PROCESSORS : max);
exec = (ThreadPoolExecutor) Executors.newFixedThreadPool(max);
}
/**
* Return the number of tasks in the queue that are running. Note this
* number is constantly changing so check than act is not really recommended.
*
* @return a rough number of queued and active tasks
*/
public int getTaskCount() {
synchronized (mutex) {
for (Future<?> f : bagOfFutures) {
if (f.isDone()) {
bagOfFutures.remove(f);
}
}
return bagOfFutures.size();
}
}
// WLH 17 Dec 2001
/**
* Remove this task from the tread pool.
*
* @param r
* the runnable to remove from the queue
*/
public void remove(Runnable r) {
exec.remove(r);
}
/**
* Has the thread pool been closed?
*
* @return <tt>true</tt> if the pool has been terminated.
*/
public boolean isTerminated() {
return exec.isTerminated();
}
/**
* Utility method to print out the tasks
*/
public void printPool() {
System.err.println("Busy Tasks:");
for (Future<?> f : bagOfFutures) {
if (!f.isDone()) {
System.out.println(f.toString());
}
}
System.err.println("Completed Tasks:");
for (Future<?> f : bagOfFutures) {
if (f.isDone()) {
System.out.println(f.toString());
}
}
}
/**
* Add a task to the queue; tasks are executed as soon as a thread is
* available, in the order in which they are submitted
*
* @param r
* the runnable that will be executed by this thread pool.
*/
public void queue(Runnable r) {
Future<?> submit = exec.submit(r);
bagOfFutures.add(submit);
// While we are at it, clean out the bag of completed tasks.
for (Future<?> f : bagOfFutures) {
if (f.isDone()) {
bagOfFutures.remove(f);
}
}
}
/**
* Wait for currently-running tasks to finish. Blocks while the internal queue
* of tasks is completed.
*
* @return true
*/
public boolean waitForTasks() {
// Lock the thread pool and drain all pending task.
synchronized (mutex) {
for (Future<?> f : bagOfFutures) {
try {
f.get();
// Tom & Don TODO: handle the exception in whatever way you think is
// best. The TheadPool previously just swallowed exceptions so that is
// how I am leaving it, but in principle, this is not advisable.
} catch (InterruptedException e) {
// http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
// The runnable threw exception.
//e.printStackTrace();
}
}
// We are now clear of futures.
bagOfFutures.clear();
}
return true;
}
/**
* Set the maximum number of pooled threads
*
* @param num
* the number of threads
*
* @throws Exception
*/
public void setThreadMaximum(int num) throws Exception {
exec.setCorePoolSize(num);
}
/** Shut down this thread pool. */
public void stopThreads() {
exec.shutdown();
}
}