Why a Thread Pool?

multi-thread program에서 실행되는 thread의 수가 많아질수록 실행되는데 걸리는 시간보다 thread간에 처리를 옮기는 데 시간을 더 많이 소비하게된다.
이 문제를 해결하기 위해 ThreadPool을 사용한다. Java에는 ThreadPool이 없기 때문에 Programmer가 구현해야 한다.
다음은 ThreadPool의 간단한 예제를 소개한다. 원문기사는 reference를 참조하기 바란다.

Implementing the Thread Pool

소스는 ThreadPool.java와 WorkerThread.java, Done.java로 구성되어 있고 Test class로 TestWorkerThread.java와 TestThreadPool.java가 있다.
thread pool은 WorkerThread Object를 배열로 가지고 있다. 이 Object는 pool을 구성하는 개별 thread들이다.
task가 발생하면 WorkerThread가 task를 실행하고 종료한다. task를 담당할 thread가 부족하게 되면, thread가 이용가능할때까지 task는 미완료된 상태로 남게된다.

처음에 ThreadPool을 생성하게 되면, WorkerThread는 정지된 상태에서 task를 기다린다. ThreadPool에 WorkerThread를 할당할 때에는 ThreadPool의 assign메소드를 이용한다.

Are We Done Yet?

thread pool이 task를 완료했는지 알려면 몇가지를 체크해야 한다. 첫째는 assignment array가 비어있어야 한다.
하지만 이것은 필요조건이지 충분조건은 아니다. 비어있다고 해도 pool안에는 실행되고 있는 WorkerThread가 존재할수도 있다.
thread pool이 작업완료 되었는지 검사하는 class가 Done.java 클래스이다. 하지만 사용할 때에는 직접 접근하지 않아도 된다.
단지 task를 assign으로 할당하고 complete메소드를 부르기만 하면 된다. Done class는 두 메소드를 가지고 있다.
WorkerThread가 시작될때 불리는 workerBegin메소드와 종료될 때 불리는 workerEnd메소드가 그것이다.
다음으로 thread pool example을 살펴본다.

ThreadPool.java

package thread;
 
import java.util.ArrayList;
import java.util.Collection;
 
/**
 * 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();
	 }
}

WorkerThread.java

package thread;
 
/**
 * 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);
	 }
}

Done.java

package thread;
 
/**
 * 
 * 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;
	 }
 
}

TestWorkerThread.java

package thread;
 
/**
 * 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) {
			   }
		  }
	 }
}

TestThreadPool.java

package thread;
 
/**
 * thread pool
 * 
 * @author Jeff Heaton (http://www.jeffheaton.com)
 * @version 1.0
 */
public class TestThreadPool {
	 /**
	  * 
	  * 
	  * @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.");
	 }
}

reference


QR Code
QR Code study:java:threadpool (generated for current page)