package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* A generic class that processes a list of {@link Runnable} one-by-one using
* one or more {@link Thread}-instances. The number of instances varies between
* 1 and {@link #WORKER_THREAD_MAX_COUNT} (default: 8). If an instance is idle
* more than {@link #WORKER_THREAD_TIMEOUT} seconds (default: 30), the instance
* ends itself.
*
* @author Jan Peter Stotz
*/
public class JobDispatcher {
private static JobDispatcher instance;
/**
* @return the singelton instance of the {@link JobDispatcher}
*/
public static JobDispatcher getInstance() {
// for speed reasons we check if the instance has been created
// one time before we enter the synchronized section...
if (instance != null)
return instance;
synchronized (JobDispatcher.class) {
// ... and for thread safety reasons one time inside the
// synchronized section.
if (instance != null)
return instance;
new JobDispatcher();
return instance;
}
}
private JobDispatcher() {
instance = this;
addWorkerThread().firstThread = true;
}
protected BlockingQueue<Runnable> jobQueue = new LinkedBlockingQueue<Runnable>();
public static int WORKER_THREAD_MAX_COUNT = 8;
/**
* Specifies the time span in seconds that a worker thread waits for new
* jobs to perform. If the time span has elapsed the worker thread
* terminates itself. Only the first worker thread works differently, it
* ignores the timeout and will never terminate itself.
*/
public static int WORKER_THREAD_TIMEOUT = 30;
/**
* Total number of worker threads currently idle or active
*/
protected int workerThreadCount = 0;
/**
* Number of worker threads currently idle
*/
protected int workerThreadIdleCount = 0;
/**
* Just an id for identifying an worker thread instance
*/
protected int workerThreadId = 0;
/**
* Removes all jobs from the queue that are currently not being processed.
*/
public void cancelOutstandingJobs() {
jobQueue.clear();
}
public void addJob(Runnable job) {
try {
jobQueue.put(job);
if (workerThreadIdleCount == 0 && workerThreadCount < WORKER_THREAD_MAX_COUNT)
addWorkerThread();
} catch (InterruptedException e) {
}
}
protected JobThread addWorkerThread() {
JobThread jobThread = new JobThread(++workerThreadId);
synchronized (this) {
workerThreadCount++;
}
return jobThread;
}
public class JobThread extends Thread {
Runnable job;
boolean firstThread = false;
public JobThread(int threadId) {
super("OSMJobThread " + threadId);
setDaemon(true);
job = null;
start();
}
@Override
public void run() {
executeJobs();
synchronized (instance) {
workerThreadCount--;
}
}
protected void executeJobs() {
while (!isInterrupted()) {
try {
synchronized (instance) {
workerThreadIdleCount++;
}
if (firstThread)
job = jobQueue.take();
else
job = jobQueue.poll(WORKER_THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
return;
} finally {
synchronized (instance) {
workerThreadIdleCount--;
}
}
if (job == null)
return;
try {
job.run();
job = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}