The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Trail: Essential Java Classes
Lesson: Threads: Doing Two or More Tasks At Once

Thread Pools

A thread pool is a managed collection of threads that are available to perform tasks. Thread pools usually provide: In addition, thread pools relieve you from having to manage the life cycle of threads. They allow to take advantage of threading, but focus on the tasks that you want the threads to perform, instead of the thread mechanics.

To use thread pools, you instantiate an implementation of the ExecutorService interface and hand it a set of tasks. The choices of configurable thread pool implementations are ThreadPoolExecutor and ScheduledThreadPoolExecutor. These implementations allow you to set the core and maximum pool size, the type of data structure used to hold the tasks, how to handle rejected tasks, and how to create and terminate threads. However, we recommend that you use the more convenient factory methods of the Executors class listed in the following table. These methods preconfigure settings for the most common usage scenarios.

Executors Thread Pool Factory Methods
Method Description
newFixedThreadPool(int) Creates a fixed size thread pool.
newCachedThreadPool Creates unbounded thread pool, with automatic thread reclamation.
newSingleThreadExecutor Creates a single background thread.
Here is a runnable task, called WorkerThread. This task performs some work and then periodically reports what percent of the work it has completed:
public class WorkerThread implements Runnable {
    private int workerNumber;

    WorkerThread(int number) {
        workerNumber = number;
    }

    public void run() {
        for (int i=0;i<=100;i+=20) {
        // Perform some work ...
            System.out.println("Worker number: " + workerNumber
                + ", percent complete: " + i );
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
            }
        }
    }
}
In ThreadPoolTest, we can specify the number of worker threads to create and the size of the thread pool that will be used to run the threads. This example uses a fixed thread pool so that you can observe the effect of running the program with fewer threads than tasks:
import java.util.concurrent.*;
public class ThreadPoolTest {
    public static void main(String[] args) {
        int numWorkers = Integer.parseInt(args[0]);
        int threadPoolSize = Integer.parseInt(args[1]);
    
        ExecutorService tpes =
            Executors.newFixedThreadPool(threadPoolSize);
    
        WorkerThread[] workers = new WorkerThread[numWorkers];
        for (int i = 0; i < numWorkers; i++) {
            workers[i] = new WorkerThread(i);
            tpes.execute(workers[i]);
        }
        tpes.shutdown();
    }
}
Here is the result of running the test with 4 workers and a pool of 2 threads:
% java ThreadPoolTest 4 2
Worker number: 0, percent complete: 0
Worker number: 1, percent complete: 0
Worker number: 0, percent complete: 20
Worker number: 0, percent complete: 40
Worker number: 1, percent complete: 20
Worker number: 0, percent complete: 60
Worker number: 0, percent complete: 80
Worker number: 0, percent complete: 100
Worker number: 1, percent complete: 40
Worker number: 1, percent complete: 60
Worker number: 2, percent complete: 0
Worker number: 1, percent complete: 80
Worker number: 2, percent complete: 20
Worker number: 2, percent complete: 40
Worker number: 1, percent complete: 100
Worker number: 2, percent complete: 60
Worker number: 2, percent complete: 80
Worker number: 2, percent complete: 100
Worker number: 3, percent complete: 0
Worker number: 3, percent complete: 20
Worker number: 3, percent complete: 40
Worker number: 3, percent complete: 60
Worker number: 3, percent complete: 80
Worker number: 3, percent complete: 100
Notice how workers 0 and 1 are assigned to the two threads in the pool and they alternately run to completion and then tasks 2 and 3 are assigned to the threads.

Like most of the other tasks in this chapter, WorkerThread implements the Runnable interface. Another way to create a task is to implement the Callable interface. A Callable is more flexible than a Runnable because it can return a value and throw an exception. To implement a Callable, you provide the call method, which returns a value, in this case an Integer that represents the tasks number:

import java.util.concurrent.*;
public class CallableWorkerThread implements
    Callable<Integer> {
    private int workerNumber;

    CallableWorkerThread(int number) {
        workerNumber = number;
    }

    public Integer call() {
        for (int i = 0; i <= 100; i += 20) {
            // Perform some work ...
            System.out.println("Worker number: " + workerNumber
                + ", percent complete: " + i );
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
            }
        }
        return(workerNumber);
    }
}
ThreadPoolTest2 uses the CachedThreadPool executor service which creates as many threads as needed but reuses previously constructed threads if available. You use the submit method to ask an executor service to run a Callable. This method returns a Future object which gives you control over the task; you can use the Future to retrieve the result of running the task, monitor the task, and cancel the task. For example, to gain access to the return result, simply call the method get:
public class ThreadPoolTest2 {
    public static void main(String[] args) {
        int numWorkers = Integer.parseInt(args[0]);

        ExecutorService tpes =
            Executors.newCachedThreadPool();
        CallableWorkerThread workers[] = 
            new CallableWorkerThread[numWorkers];
        Future futures[] = new Future[numWorkers];
        
        for (int i = 0; i < numWorkers; i++) {
            workers[i] = new CallableWorkerThread(i);
            futures[i]=tpes.submit(workers[i]);
        }
        for (int i = 0; i < numWorkers; i++) {
            try {
                System.out.println("Ending worker: " +
                    futures[i].get());
            } catch (Exception e) {}
        }
    }
}
Heres the result of running ThreadPoolTest2. Notice how each worker task is immediately assigned a thread to run in. As each task completes, it returns its identifier, which ThreadPoolTest2 retrieves from the worker task futures:
% java ThreadPoolTest2 4
Worker number: 0, percent complete: 0
Worker number: 1, percent complete: 0
Worker number: 2, percent complete: 0
Worker number: 3, percent complete: 0
Worker number: 3, percent complete: 20
Worker number: 3, percent complete: 40
Worker number: 3, percent complete: 60
Worker number: 1, percent complete: 20
Worker number: 0, percent complete: 20
Worker number: 1, percent complete: 40
Worker number: 2, percent complete: 20
Worker number: 3, percent complete: 80
Worker number: 0, percent complete: 40
Worker number: 2, percent complete: 40
Worker number: 2, percent complete: 60
Worker number: 1, percent complete: 60
Worker number: 3, percent complete: 100
Worker number: 2, percent complete: 80
Worker number: 2, percent complete: 100
Worker number: 0, percent complete: 60
Worker number: 0, percent complete: 80
Worker number: 0, percent complete: 100
Worker number: 1, percent complete: 80
Ending worker: 0
Worker number: 1, percent complete: 100
Ending worker: 1
Ending worker: 2
Ending worker: 3

Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.