/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.concurrency;
import org.ws4d.java.structures.Iterator;
import org.ws4d.java.structures.LinkedList;
import org.ws4d.java.structures.List;
import org.ws4d.java.util.Log;
/**
* Implements a simple thread pool which allows dynamic creation of worker
* threads and automatic disposal of unused worker threads after the given
* period of time.
* <p>
* Usage example:
* </p>
* <code>
* <p>ThreadPool myThreadPool = new ThreadPool (3, 5000);<br />
* myThreadPool.executeOrAbort(runnable1); // essential tasks, which must be started immediately or aborted<br />
* myThreadPool.execute(runnable2); ... // common tasks, which will be started as soon as possible <br />
* myThreadPool.execute(runnableN);<br />
* myThreadPool.shutdown();</p>
* </code>
*/
public class ThreadPool {
/**
* default size of the thread pool
*/
private static int DEFAULT_SIZE = 10;
/**
* default time to live for idle available thread pool workers
*/
private static long DEFAULT_TIMEOUT = 10000;
/**
* list of idle worker threads
*/
private final List idleThreads = new LinkedList();
/**
* list of active worker threads
*/
private final List activeThreads = new LinkedList();
/**
* queue with tasks waiting for any available worker thread
*/
private final List waitingTasks = new LinkedList();
/**
* maximal number of threads in the pool
*/
private volatile int size;
/**
* life duration of idle available thread pool workers before which the idle
* threads will be disposed of
*/
private final long timeout;
/**
* Internal lock object
*/
private final Object lock = new Object();
/**
* The constructor of the ThreadPool class, creating a thread pool with
* default size and default timeout.
*/
public ThreadPool() {
this(DEFAULT_SIZE, DEFAULT_TIMEOUT);
}
/**
* The constructor of the ThreadPool class.
*
* @param size maximal number of threads in the pool
* @param timeout life duration of idle thread pool worker
*/
public ThreadPool(int size, long timeout) {
this.size = size;
this.timeout = timeout;
}
/**
* The constructor of the ThreadPool class with the default life duration of
* idle threads
*
* @param size maximal number of threads in the pool
*/
public ThreadPool(int size) {
this(size, DEFAULT_TIMEOUT);
}
/**
* Signalizes to the thread pool that a worker thread has finished his task
* and is ready for further tasks. If there is no tasks waiting in the queue
* for execution, the worker thread is placed on the list of available
* thread workers. Else the thread worker gets the first waiting task
* assigned for the execution.
*
* @param t worker thread which has finished its task
*/
private void signalAvailability(WorkerThread w) {
synchronized (lock) {
if (waitingTasks.size() > 0) {
w.setTask((Runnable) waitingTasks.remove(0));
} else {
idleThreads.add(w);
activeThreads.remove(w);
w.setTask(null);
}
}
}
/**
* Signalizes to the thread pool that a thread worker is going to terminate
* itself.
*
* @param t worker thread signaling the termination
*/
private void signalTermination(WorkerThread w) {
idleThreads.remove(w);
}
/**
* Assigns tasks to the thread pool for execution. Use this method for
* common non-critical tasks. The tasks will be started as soon as possible.
*
* @param task runnable which is assigned to the thread pool
*/
public void execute(Runnable task) {
synchronized (lock) {
if (!tryAllocation(task)) {
waitingTasks.add(task);
}
}
}
/**
* Assigns tasks to the thread pool for execution. Use this method for
* essential tasks, which have to be immediately started or else aborted.
*
* @param task runnable which is assigned to the thread pool
* @return boolean returns true only if the runnable was started
* immediately, false if the runnable could not be started
* immediately and was aborted
*/
public boolean executeOrAbort(Runnable task) {
synchronized (lock) {
if (!tryAllocation(task)) {
return false;
}
return true;
}
}
private boolean tryAllocation(Runnable task) {
if (idleThreads.size() == 0) {
if (activeThreads.size() < size) {
WorkerThread w = new WorkerThread();
allocate(w, task);
w.start();
return true;
}
} else {
WorkerThread w = (WorkerThread) idleThreads.remove(0);
allocate(w, task);
return true;
}
return false;
}
private void allocate(WorkerThread w, Runnable task) {
activeThreads.add(w);
w.setTask(task);
}
/**
* Disposes of the thread pool. New tasks will be ignored. The currently
* running threads will be executed until the end.
*/
public void shutdown() {
Thread thisThread = Thread.currentThread();
List availableThreads = new LinkedList();
synchronized (lock) {
for (Iterator it = activeThreads.iterator(); it.hasNext();) {
WorkerThread w = (WorkerThread) it.next();
availableThreads.add(w);
w.shutdown();
}
for (Iterator it = idleThreads.iterator(); it.hasNext();) {
WorkerThread w = (WorkerThread) it.next();
availableThreads.add(w);
w.shutdown();
it.remove();
}
}
for (Iterator it = availableThreads.iterator(); it.hasNext();) {
WorkerThread w = (WorkerThread) it.next();
if (w != thisThread) {
try {
w.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* Implements a work delegation thread - worker thread class.
*/
class WorkerThread extends Thread {
private volatile boolean shutdown = false;
/**
* task that is to be executed by the worker thread
*/
private volatile Runnable task;
public void run() {
while (true) {
synchronized (this) {
if (!shutdown && (task == null)) {
try {
wait(timeout);
} catch (InterruptedException e) {
// swallow
}
if (shutdown) {
return;
}
}
}
synchronized (lock) {
if (task == null) {
shutdown = true;
signalTermination(this);
return;
}
}
try {
task.run();
} catch (Exception e) {
Log.error("Exception occurred while running thread. " + e.getMessage());
Log.printStackTrace(e);
} finally {
signalAvailability(this);
}
}
}
/**
* Setter method for the task variable
*
* @param task task to be assigned to the worker thread
*/
public synchronized void setTask(Runnable task) {
this.task = task;
notify();
}
public synchronized void shutdown() {
shutdown = true;
notify();
}
}
}