As you can see, a thread pool is a useful way to limit the amount of threads that a Java program will be using. This article presented a complete thread pool that can be used with Java. This thread pool can easily become the starting point for any application that you create, which requires a thread pool.
Listing 1—The ThreadPool Class
package com.heaton.threads; import java.util.*; /** * Java Thread Pool * * This is a thread pool that for Java, it is * simple to use and gets the job done. This program and * all supporting files are distributed under the Limited * GNU Public License (LGPL, http://www.gnu.org). * * This is the main class for the thread pool. You should * create an instance of this class and assign tasks to it. * * For more information visit http://www.jeffheaton.com. * * @author Jeff Heaton (http://www.jeffheaton.com) * @version 1.0 */ public class ThreadPool { /** * The threads in the pool. */ protected Thread threads[] = null; /** * The backlog of assignments, which are waiting * for the thread pool. */ Collection assignments = new ArrayList(3); /** * A Done object that is used to track when the * thread pool is done, that is has no more work * to perform. */ protected Done done = new Done(); /** * The constructor. * * @param size How many threads in the thread pool. */ public ThreadPool(int size) { threads = new WorkerThread[size]; for (int i=0;i<threads.length;i++) { threads[i] = new WorkerThread(this); threads[i].start(); } } /** * Add a task to the thread pool. Any class * which implements the Runnable interface * may be assienged. When this task runs, its * run method will be called. * * @param r An object that implements the Runnable interface */ public synchronized void assign(Runnable r) { done.workerBegin(); assignments.add(r); notify(); } /** * Get a new work assignment. * * @return A new assignment */ public synchronized Runnable getAssignment() { try { while ( !assignments.iterator().hasNext() ) wait(); Runnable r = (Runnable)assignments.iterator().next(); assignments.remove(r); return r; } catch (InterruptedException e) { done.workerEnd(); return null; } } /** * Called to block the current thread until * the thread pool has no more work. */ public void complete() { done.waitBegin(); done.waitDone(); } protected void finalize() { done.reset(); for (int i=0;i<threads.length;i++) { threads[i].interrupt(); done.workerBegin(); threads[i].destroy(); } done.waitDone(); } } /** * The worker threads that make up the thread pool. * * @author Jeff Heaton * @version 1.0 */ class WorkerThread extends Thread { /** * True if this thread is currently processing. */ public boolean busy; /** * The thread pool that this object belongs to. */ public ThreadPool owner; /** * The constructor. * * @param o the thread pool */ WorkerThread(ThreadPool o) { owner = o; } /** * Scan for and execute tasks. */ public void run() { Runnable target = null; do { target = owner.getAssignment(); if (target!=null) { target.run(); owner.done.workerEnd(); } } while (target!=null); } }
Listing 2—The Done Class
package com.heaton.threads; /** * * This is a thread pool for Java, it is * simple to use and gets the job done. This program and * all supporting files are distributed under the Limited * GNU Public License (LGPL, http://www.gnu.org). * * This is a very simple object that * allows the TheadPool to determine when * it is done. This object implements * a simple lock that the ThreadPool class * can wait on to determine completion. * Done is defined as the ThreadPool having * no more work to complete. * * Copyright 2001 by Jeff Heaton * * @author Jeff Heaton (http://www.jeffheaton.com) * @version 1.0 */ public class Done { /** * The number of Worker object * threads that are currently working * on something. */ private int _activeThreads = 0; /** * This boolean keeps track of if * the very first thread has started * or not. This prevents this object * from falsely reporting that the ThreadPool * is done, just because the first thread * has not yet started. */ private boolean _started = false; /** * This method can be called to block * the current thread until the ThreadPool * is done. */ synchronized public void waitDone() { try { while ( _activeThreads>0 ) { wait(); } } catch ( InterruptedException e ) { } } /** * Called to wait for the first thread to * start. Once this method returns the * process has begun. */ synchronized public void waitBegin() { try { while ( !_started ) { wait(); } } catch ( InterruptedException e ) { } } /** * Called by a Worker object * to indicate that it has begun * working on a workload. */ synchronized public void workerBegin() { _activeThreads++; _started = true; notify(); } /** * Called by a Worker object to * indicate that it has completed a * workload. */ synchronized public void workerEnd() { _activeThreads--; notify(); } /** * Called to reset this object to * its initial state. */ synchronized public void reset() { _activeThreads = 0; } }
Listing 3—The Example Worker Thread
import com.heaton.threads.*; /** * This class shows an example worker thread that can * be used with the thread pool. It demonstrates the main * points that should be included in any worker thread. Use * this as a starting point for your own threads. * * @author Jeff Heaton (http://www.jeffheaton.com) * @version 1.0 */ public class TestWorkerThread implements Runnable { static private int count = 0; private int taskNumber; protected Done done; /** * * @param done */ TestWorkerThread() { count++; taskNumber = count; } public void run() { for (int i=0;i<100;i+=2) { System.out.println("Task number: " + taskNumber + ",percent complete = " + i ); try { Thread.sleep((int)(Math.random()*500)); } catch (InterruptedException e) { } } } }
Listing 4—The ThreadPool Class
import com.heaton.threads.*; /** * Main class used to test the thread pool. * * @author Jeff Heaton (http://www.jeffheaton.com) * @version 1.0 */ public class TestThreadPool { /** * Main entry point. * * @param args No arguments are used. */ public static void main(String args[]) { ThreadPool pool = new ThreadPool(10); for (int i=1;i<25;i++) { pool.assign(new TestWorkerThread()); } pool.complete(); System.out.println("All tasks are done."); }