/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * Copyright (C) 2007 Intevation GmbH * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.workbench.ui.renderer; import java.util.LinkedList; import java.util.ArrayList; /** * This thread queue executes at maximum N Runnables in parallel * were N is a given number of worker threads that should be used. * If N threads are running and busy each further incoming * Runnable is queued until one of the threads has finished its current job. * If a worker thread becomes idle (no more job in the queue) * it is hold alive for 5 seconds. If during this period of time * no new Runnable is enqueued the worker thread dies. * * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) */ public class ThreadQueue { /** The time a worker thread stays alive if idle */ public static final long WORKER_STAY_ALIVE_TIME = 5000L; /** * Worker thread. Fetches Runnable from the surrounding * ThreadQueue instance. */ protected class Worker extends Thread { public void run() { try { for (;;) { Runnable runnable; synchronized (queuedRunnables) { if (queuedRunnables.isEmpty()) { ++waitingThreads; try { queuedRunnables.wait(WORKER_STAY_ALIVE_TIME); } catch (InterruptedException ie) { } finally { --waitingThreads; } // if still empty -> die! if (queuedRunnables.isEmpty()) break; } if (disposed) break; runnable = (Runnable)queuedRunnables.remove(); } // synchronized queuedRunnables try { runnable.run(); } catch (Exception e) { e.printStackTrace(); } // check if we are the last of the mohicans ... boolean lastRunningThread; synchronized (runningThreads) { lastRunningThread = runningThreads[0] == 1; } if (lastRunningThread) { fireAllRunningThreadsFinished(); } } // for (;;) } finally { // guarantee that counter goes down synchronized (runningThreads) { --runningThreads[0]; } } } } // class Worker /** * If the number of running threads goes down to zero * implementations of this interface are able to be informed. */ public interface Listener { void allRunningThreadsFinished(); } // interface Listener /** Number of running threads */ protected int [] runningThreads = new int[1]; /** max. Number of threads running parallel */ protected int maxRunningThreads; /** Number of threads that are currently idle */ protected int waitingThreads; /** The queue of Runnables jobs waiting to be run */ protected LinkedList queuedRunnables; /** Singals that the ThreadQueue is going to quit */ protected boolean disposed; /** List of Listeners */ protected ArrayList listeners = new ArrayList(); /** * Creates a ThreadQueue with one worker thread. */ public ThreadQueue() { this(1); } /** Creates a ThreadQueue with a given number of worker threads. * @param maxRunningThreads the max. number of threads to be run parallel. */ public ThreadQueue(int maxRunningThreads) { this.maxRunningThreads = Math.max(1, maxRunningThreads); queuedRunnables = new LinkedList(); } /** * Adds a Listener to this ThreadQueue. * @param listener the listener to add. */ public synchronized void add(Listener listener) { if (listener != null) listeners.add(listener); } /** * Removes a Listener from this ThreadQueue. * @param listener the listener to be removed. */ public synchronized void remove(Listener listener) { if (listener != null) listeners.remove(listener); } /** * Informs Listeners of the fact that the number of running threads * went to zero. */ protected void fireAllRunningThreadsFinished() { ArrayList copy; synchronized (this) { copy = new ArrayList(listeners); } for (int i = copy.size()-1; i >= 0; --i) ((Listener)copy.get(i)).allRunningThreadsFinished(); } /** * The number of currently running worker threads. * @return number of currently running worker threads. */ public int runningThreads() { synchronized (runningThreads) { return runningThreads[0]; } } /** * The number of currently running worker threads. * Alias for runningThreads() * @return number of currently running worker threads. */ public int getRunningThreads() { return runningThreads(); } /** * The number of currently waiting Runnables. * @return number of currently waiting Runnables. */ public int waitingRunnables() { synchronized (runningThreads) { return queuedRunnables.size(); } } /** * The number of currently idle worker threads. * @return number of currently idle worker threads. */ public int waitingThreads() { synchronized (queuedRunnables) { return waitingThreads; } } /** * Adds a Runnables to the queue. It will be run in one * of the worker threads. * @param runnable The Runnables to add */ public void add(Runnable runnable) { int waiting; synchronized (queuedRunnables) { if (disposed) return; waiting = waitingThreads; queuedRunnables.add(runnable); queuedRunnables.notify(); } // synchronized (queuedRunnables) synchronized (runningThreads) { // if waitingThreads == 1 then // the queuedRunnables.notify() should have waked it up. if (waitingThreads < 2 && runningThreads[0] < maxRunningThreads) { ++runningThreads[0]; Worker w = new Worker(); w.setDaemon(true); w.start(); } } // synchronized (runningThreads) } /** * Empties the queue of waiting Runnables. */ public void clear() { synchronized (queuedRunnables) { queuedRunnables.clear(); } } /** * Shuts down the ThreadQueue. */ public void dispose() { synchronized (queuedRunnables) { disposed = true; queuedRunnables.clear(); // wakeup idle threads queuedRunnables.notifyAll(); } synchronized (this) { listeners.clear(); } } } // end of file