站内搜索: 请输入搜索关键词
当前页面: 在线文档首页 > Java Tutorial 5.0 英文版

Thread Pools - Java Tutorial 5.0 英文版

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 the following:
  • Improved performance when executing large numbers of tasks as a result of reduced per-task invocation overhead.
  • A way of bounding the resources, including threads, consumed when executing a collection of tasks.
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 thread mechanics.

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

Factory Methods in the Executors Class
Method Description
newFixedThreadPool(int) (in the API reference documentation) Creates a fixed-size thread pool.
newCachedThreadPool (in the API reference documentation) Creates an unbounded thread pool with automatic thread reclamation.
newSingleThreadExecutor (in the API reference documentation) Creates a single background thread.
Here is a runnable task called WorkerThread (in a .java source file). The 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.format("Worker number: %d, percent complete: %d%n",
                workerNumber, i);
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) { }
        }
    }
}
In ThreadPoolTest (in a .java source file), you can specify the number of worker threads to create and the size of the thread pool that will be used to run the threads. The following 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 that they alternately run to completion, then tasks 2 and 3 are assigned to the threads.

Like most of the other tasks in this chapter, WorkerThread implements the Runnable (in the API reference documentation) interface. Another way to create a task is to implement the Callable (in the API reference documentation) interface, as shown in the following example, CallableWorkerThread (in a .java source file). 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 task's 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.format("Worker number: %d, percent complete: %d%n",
                workerNumber, i);
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {}
        }
        return(workerNumber);
    }
}
ThreadPoolTest2 (in a .java source file) 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 (in the API reference documentation) object, which gives you control over the task; you can use Future to retrieve the result of running the task, to monitor the task, and to 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<Integer> 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.format("Ending worker: %d%n",
                    futures[i].get());
            } catch (Exception e) {}
        }
    }
}
Here's the result of running ThreadPoolTest2. Notice how each worker's 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.