All computer source code presented on this page, unless it includes attribution to another author, is provided by Ed Halley under the Artistic License. Use such code freely and without any expectation of support. I would like to know if you make anything cool with the code, or need questions answered.
python/
    bindings.py
    boards.py
    buzz.py
    caches.py
    cards.py
    constraints.py
    csql.py
    english.py
    getch.py
    getopts.py
    gizmos.py
    goals.py
    improv.py
    interpolations.py
    namespaces.py
    nihongo.py
    nodes.py
    octalplus.py
    patterns.py
    physics.py
    pids.py
    pieces.py
    quizzes.py
    recipes.py
    relays.py
    romaji.py
    ropen.py
    sheets.py
    stores.py
    strokes.py
    subscriptions.py
    svgbuild.py
    testing.py
    things.py
    timing.py
    ucsv.py
    useful.py
    uuid.py
    vectors.py
    weighted.py
java/
    CSVReader.java
    CSVWriter.java
    GlobFilenameFilter.java
    RegexFilenameFilter.java
    StringBufferOutputStream.java
    ThreadSet.java
    Throttle.java
    TracingThread.java
    Utf8ConsoleTest.java
    droid/
        ArrangeViewsTouchListener.java
        DownloadFileTask.java
perl/
    CVQM.pm
    Kana.pm
    Typo.pm
cxx/
    CCache.h
    equalish.cpp
Download ThreadSet.java
package cc.halley.java.util;

import java.util.Iterator;
import java.util.HashSet;
import java.util.Set;

/**
 * A set of thread instances to be monitored together.
 * 
 * <p>A <tt>ThreadGroup</tt> is a passive structure that queries the
 * underlying operating system for various membership relationships.  Each
 * thread is created into a thread group, but the Java thread group itself
 * doesn't get notified or updated to indicate the change in membership.
 * Querying the thread group for member threads' state is very limited.
 * 
 * <p>This <tt>ThreadSet</tt> class is a Java construct that manages a set
 * of <tt>Thread</tt> instances in a proactive, formal manner.  It does not
 * rely on the operating system's understanding of thread relationships.
 * You create threads and specifically add them to the <tt>ThreadSet</tt>
 * instance.
 */
public class ThreadSet
{
        public ThreadSet()
                { ; }

        protected Set pending = new HashSet();
        protected Set started = new HashSet();
        protected Set resting = new HashSet();

        private static Thread _anyFromSet(Set set)
        {
                if (set.isEmpty())
                        return null;
                Iterator it = set.iterator();
                return (Thread)it.next();
        }
        
        /**
         * Adds the thread to the threadset to be managed.
         * 
         * <p>The thread must not be started before adding it to the set.
         * @param thread a thread object to be managed in the set
         * @return the given thread
         */
        public synchronized Thread addThread(Thread thread)
        {
                if (resting.contains(thread) ||
                        started.contains(thread) ||
                        pending.contains(thread))
                {
                        throw new IllegalStateException("Can't add thread again.");
                }
                // We can't detect all bad cases with pre-started threads.
                if (thread.isAlive())
                        throw new IllegalStateException("Thread already started.");
                pending.add(thread);
                return thread;
        }

        /**
         * Removes a thread from the threadset management.
         * 
         * <p>The thread is not started nor suspended by its removal from a set.
         * @param thread the thread to be removed
         */
        public synchronized void removeThread(Thread thread)
        {
                if (resting.contains(thread))
                        resting.remove(thread);
                else if (started.contains(thread))
                        started.remove(thread);
                else if (pending.contains(thread))
                        pending.remove(thread);
                else
                        throw new IllegalStateException("Can't remove unknown thread.");
        }

        /**
         * Start the given thread's execution.
         * 
         * <p>The given thread is added to the set if it was not already added.
         * It is an error to start a thread that has already been started.
         * @param thread the thread instance to be started
         * @throws IllegalStateException if the thread has already been started
         */
        public synchronized void startThread(Thread thread)
        {
                if (resting.contains(thread))
                        throw new IllegalStateException("Can't start a finished thread.");
                if (started.contains(thread))
                        throw new IllegalStateException("Can't start a thread twice.");
                if (pending.contains(thread))
                        pending.remove(thread);
                started.add(thread);
                thread.start();
        }

        /**
         * Acknowledge that a thread has completed.
         * 
         * <p>This does not force the thread to quit, or wait for it to finish.
         * It simply acknowledges that the thread is known to be finished, so
         * that it will not be returned again from the
         * <tt>anyNewlyFinishedThread()</tt> method.
         * @param thread
         */
        public synchronized void finishThread(Thread thread)
        {
                if (resting.contains(thread))
                        throw new IllegalStateException("Can't finish finished thread.");
                if (pending.contains(thread))
                        throw new IllegalStateException("Can't finish pending thread.");
                if (!started.contains(thread))
                        throw new IllegalStateException("Can't finish unknown thread.");
                if (thread.isAlive())
                        throw new IllegalArgumentException("Thread is still alive.");
                started.remove(thread);
                resting.add(thread);
        }

        /**
         * Start an arbitrary pending thread.
         * @return the thread that has been started, or null if none were pending
         */
        public Thread startAnyThread()
        {
                Thread thread = anyUnstartedThread();
                if (thread != null)
                        startThread(thread);
                return thread;
        }
        
    /**
     * Fetches an arbitrary thread that has not yet been started.
     * @return a thread which has not been started, if any, or null
     */
    public Thread anyUnstartedThread()
    {
        while (true)
        {
                if (pending.isEmpty())
                        return null;
                Thread thread = _anyFromSet(pending);
                if (thread.isAlive())
                {
                        pending.remove(thread);
                        started.add(thread);
                        continue;
                }
                return thread;
        }
    }
        
    /**
     * Fetches an arbitrary thread that has been finished and reported.
     * This routine only finds threads previously acknowledged with a call to
     * <tt>finishThread()</tt>.  There is no guarantee that this routine
     * alone will traverse all finished threads.
     * @return a thread which has been explicitly finished, or null if none
     */
    public Thread anyWellFinishedThread()
        { return _anyFromSet(resting); }

    /**
     * Checks if there are any threads in the group which have finished.
     * It is up to the caller to use the <tt>finishThread()</tt> method,
     * which moves the thread from the "newly" finished to a "well" finished
     * state.  Otherwise this method may continue to report the same thread.
     * @return a thread which has finished, or null if none
     */
    public Thread anyNewlyFinishedThread()
    {
        if (started.isEmpty())
                return null;
        Iterator it = started.iterator();
        while (it.hasNext())
        {
            Thread thread = (Thread)it.next();
                if (!thread.isAlive())
                        return thread;
        }
        return null;
    }

    /**
     * Waits until there is at least one thread that has finished.
     * It is up to the caller to use the <tt>finishThread()</tt> method,
     * which moves the thread from the "newly" finished to a "well" finished
     * state.  Otherwise this method may continue to report the same thread.
     * @return a thread which has finished, or null if none
     * @throws InterruptedException
     */
    public Thread waitForAnyThreadToFinish()
        throws InterruptedException
    {
        while (!started.isEmpty())
        {
                Thread thread = anyNewlyFinishedThread();
                if (thread != null)
                        return thread;
                
                //REVIEW: There's no way in Java to wait for one of many threads.
                // We sleep an arbitrary but short time here, as a weak solution
                // otherwise we burn 100% CPU waiting for another thread to be
                // ready.
                //
                Thread.sleep(2);
        }
        return null;
    }

}


Contact Ed Halley by email at ed@halley.cc.
Text, code, layout and artwork are Copyright © 1996-2013 Ed Halley.
Copying in whole or in part, with author attribution, is expressly allowed.
Any references to trademarks are illustrative and are controlled by their respective owners.
Make donations with PayPal - it's fast, free and secure!