Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
java_concurrency_in_practice.pdf
Скачиваний:
103
Добавлен:
02.02.2015
Размер:
6.66 Mб
Скачать

106 Java Concurrency In Practice

mostly computation, I/O, or some combination? Do they require a scarce resource, such as a JDBC connection? If you have different categories of tasks with very different behaviors, consider using multiple thread pools so each can be tuned according to its workload.

For compute intensive tasks, an Ncpu processor system usually achieves optimum utilization with a thread pool of Ncpu

+1 threads. (Even compute intensive threads occasionally take a page fault or pause for some other reason, so an

"extra" runnable thread prevents CPU cycles from going unused when this happens.) For tasks that also include I/O or other blocking operations, you want a larger pool, since not all of the threads will be schedulable at all times. In order to size the pool properly, you must estimate the ratio of waiting time to compute time for your tasks; this estimate need not be precise and can be obtained through pro filing or instrumentation. Alternatively, the size of the thread pool can be tuned by running the application using several different pool sizes under a benchmark load and observing the level of

CPU utilization.

Given these definitions:

 

= number of CPUs

 

 

= target CPU utilization,

 

 

 

 

 

 

 

compute time

 

 

 

 

= ratio of wait time to

 

 

 

 

 

 

 

 

 

 

 

The optimal pool size for keeping the processors at the desired utilization is:

כ כ

You can determine the number of CPUs using Runtime:

int N_CPUS = Runtime.getRuntime().availableProcessors();

Of course, CPU cycles are not the only resource you might want to manage using thread pools. Other resources that can contribute to sizing constraints are memory, file handles, socket handles, and database connections. Calculating pool size constraints for these types of resources is easier: just add up how much of that resource each task requires and divide that into the total quantity available. The result will be an upper bound on the pool size.

When tasks require a pooled resource such as database connections, thread pool size and resource pool size affect each other. If each task requires a connection, the effective size of the thread pool is limited by the connection pool size.

Similarly, when the only consumers of connections are pool tasks, the effective size of the connection pool is limited by

the thread pool size.

8.3. Configuring ThreadPoolExecutor

ThreadPoolExecutor provides the base implementation for the executors returned by the newCachedThreadPool, newFixedThreadPool, and newScheduled-ThreadExecutor factories in Executors. ThreadPoolExecutor is a flexible, robust pool implementation that allows a variety of customizations.

If the default execution policy does not meet your needs, you can instantiate a ThreadPoolExecutor through its constructor and customize it as you see fit; you can consult the source code for Executors to see the execution policies for the default configurations and use them as a starting point. ThreadPoolExecutor has several constructors, the most general of which is shown in Listing 8.2.

8.3.1. Thread Creation and Teardown

The core pool size, maximum pool size, and keep alive time govern thread creation and teardown. The core size is the

target size; the implementation attempts to maintain the pool at this size even when there are no tasks to execute,[2] and will not create more threads than this unless the work queue is full.[3] The maximum pool size is the upper bound on

how many pool threads can be active at once. A thread that has been idle for longer than the keep alive time becomes a candidate for reaping and can be terminated if the current pool size exceeds the core size.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]