Larry,

there is probably somebody out there (younger than us)
how says that 400ms feels slow too.

I've thought a bit about the compromise and came to the
conclusion that we don't need a make a compromise here.

We have simply to restore the behavior of the
original TheadQueue. The original one fires the
Listeners when the running threads went down to zero.
We can do the same when we're in the situation that we
are the last remaining thread with our job done.

In this case the number of running threads is
one but this measure wasn't reliable in the old
ThreadQueue too. So it doesn't matter.
But in difference to the original we keep the
worker thread alive afterwards instead of killing it.

Find attached a new version of the ThreadQueue that
implements this behavior.

regards,
Sascha

Larry Becker schrieb:
> Sascha,
> 
>   I tried one second, and it feels slow.  When I am arrowing through a
> selection of records in View/Edit Attributes it makes me wait for the
> flash before I move on.  Really, this is becoming  an issue of
> compromising the interactivity of the application to achieve some
> theoretical benefit that can't be seen or measured.
> 
> How about 400 ms?  That is about the average reaction time.
> 
> regards,
> Larry Becker
/*
 * 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 ([EMAIL PROTECTED])
 */
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
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Jump-pilot-devel mailing list
Jump-pilot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel

Reply via email to