Seven, use the thread pool

一、在任务与执行策略之间的隐性耦合

Executor decoupling execution policy framework can submit tasks to the task off. Like so many of the complex process of decoupling operations, this assertion somewhat exaggerated. Although the Executor framework for the formulation and implementation of policy modification provides considerable flexibility, but not all either; need to explicitly specify the task execution strategies, including:

  • 依赖性任务。Most tasks are independent of the correct behavior: they do not depend on the timing of the execution of other tasks, the results or other effects. When performing independent tasks in a thread pool, the thread can be changed freely 'pool size and configuration, these changes will only affect the execution performance. However, if the task presented to the thread pool need to rely on other tasks, then implicitly bound to enforce policy has brought, this time must be careful to maintain these execution strategies to avoid active issues.
  • 使用线程封闭机制的任务。Compared with the thread pool, single-threaded Executor to make a stronger commitment to concurrency. They can ensure that the task will not be performed concurrently, so that you can relax the requirements of the code thread-safe. Objects can be closed in the task thread, so that tasks performed in the thread synchronization is not required when accessing the object, even if these resources are not thread safe, no problem. Implicit in such cases are to be coupled between tasks and enforce policies - where the task requires Executor its implementation is single-threaded if the thread pool Executor changed environment from a single-threaded environment, it will lose the thread safety.
  • 对响应时间敏感的任务。GUI application response time is sensitive: if you take a long delay after clicking the button to get visible feedback, then they will feel dissatisfied. If you run a longer time to submit tasks in single-threaded Executor, or to submit multiple long-running tasks to the thread pool contains only a small number of threads, it will reduce the response by the management of the services of the Executor sex.
    Use ThreadLocal task. ThreadLocal so that each thread can have a variable of a private "version." However, as long as conditions permit, Executor free to reuse these threads. In a standard Executor implementations, when demand is low execution recovered idle thread, and when demand increases will add a new thread, and if thrown from the task in an unchecked exception, will use a new worker thread instead of throwing an exception thread. Only when the life cycle of thread local value is limited to the life cycle of the task, using ThreadLocal makes sense in the thread pool thread, but should not be used ThreadLocal value passed between tasks in a thread pool thread.
    Only when the tasks are independent of each other and the same type of performance in order to achieve the best thread pool. If the long-running mixed with a shorter run time task, unless the thread pool is large, otherwise it will cause "congestion." If the task submitted depend on other tasks, then, unless the thread pool is infinite, otherwise it will cause a deadlock. Fortunately, in a typical server applications web-based - web servers, mail servers, and file servers, their requests are usually of the same type and independent of each other.

In some missions, you need to have or exclude a specific implementation strategy. If some tasks dependent on other tasks, it will ask the thread pool is large enough to ensure that they rely on the task will not be placed in the waiting queue or rejected, while the use of closed thread mechanism task requires serial execution. By combining these documents need to be written, the future code maintenance personnel will not be executed due to the use of some kind of inappropriate policies and undermine security or activity.

  • 线程接死锁

In the thread pool, if the task is dependent on another task, it may produce a deadlock. In single-threaded Executor, if one task to another task will be submitted to the same Executor, and wait for the results to be submitted to the task, it will usually lead to a deadlock. The second task to stay in the work queue, and wait for the first a task is completed, and the first task they can not be completed because it is waiting for the completion of the second task. 在更大的线程池中,如果所有正在执行任务的线程都由 于等待其他仍处于工作队列中的任务而阻塞,那么会发生同样的问题。这种现象被称为线程饥饿死锁(Thread Starvation Deadlock),As long as the task thread pool needs to wait indefinitely for some resources or conditions that must be provided by additional tasks to the pool, for example, a task not wait for another task or return line results, non-thread pool is large enough, otherwise the thread will happen starvation deadlock.

Whenever submitted a dependent Executor task needs to be cleared to know that may arise thread "starvation" deadlock, it is necessary to limit the size or configuration limitations record thread pool Executor in code or configuration profile.

In addition to explicit restrictions on the thread pool size, but may also have some limitations due to the implicit constraints on other resources. If the application uses a connection contains 10 JDBC connection pool, and each task requires a database connection, the thread pool if only 10 threads, because when more than 10 tasks, new tasks need to wait for the release of other tasks connected .

  • 运行时间较长的任务

If the task is blocked for too long, even if a deadlock does not occur, the response of the thread pool will become worse. A long-time task execution thread pool will not only cause blockage, or even increase task execution time is shorter service time. If the number of threads in the thread pool is much smaller than the number of tasks to perform longer at steady state, then to the last possible that all of these threads will run longer execution time of the task, thus affecting the overall responsiveness.
One technique can mitigate the impact caused by the execution time of a long task, that task waiting time limited resources, rather than wait indefinitely. In most platform libraries can block method, the definition of J simultaneously limit this deduction and unlimited time version, for example Thread.join, BlockingQueue.put, CountDownLatch.await and Selector.select and so on. If the wait times out, the task can be identified as a failure, then abort the task or tasks back into the queue for subsequent execution. In this way, regardless of the final results of the task is successful, this approach can ensure that the task can always continue to go on, and the thread is released to perform some tasks can be completed faster. If the thread pool is always full of blocked task, it may also indicate the size of the thread pool is too small.

二、设置线程池的大小

Ideal sized thread pool thread pool size depends on the type of job being submitted and the nature of the deployment of the system. Solid usually not in the code
size of the thread pool may be, but should be provided by a pen home mechanism, or according to Runtime.availableProcessorscalculation movable.

Fortunately, to set the thread pool size was not difficult, just need to avoid the "too big" and "too small" these two extremes. If the thread pool is too large, then the amount of thread female competition will take place on relatively little CPU and memory consultative source, it will not only lead to more Gao's memory usage, but also may run out of resources. If the thread pool is too small, it will cause a lot of idle processor can not perform work, thus reducing throughput.

To correctly set the thread pool size, we must analyze the characteristics of the computing environment, resources, budget and tasks. How many CPU in the system deployed? How much memory? Task is computationally intensive, I / O-intensive or two can? Whether they need to connect scarce resources such as JDBC? If you need to perform different types of tasks, and behavior vary widely between them, then you should consider using multiple thread pool, so that each thread pool can be adjusted according to their workload.

对于计算密集型的任务,在拥有N个处理器的系统上,当线程池的大小为N+1时,通常能实现最优的利用率。(Even when the compute-intensive thread occasionally missing page fault or due to other reasons, the suspension of the "extra" thread can ensure CPU cycles are not wasted.)对于包含I/O操作或者其他阻塞操作的任务,由于线程并不舍一直执行,因此线程池的规模应该更大。要正确地设置线程池的大小,你必须估算出任务的等待时间与计算时间的比值。这种估算不需要很精确,并且可以通过一些分析或监控工具来获得。你还可以通过另一种方法来调节线程池的大小:在某个基准负载下,分别设置不同大小的线程池来运行应用程序,并观察CPU利用率的水平。

Given the following definitions:
N = the number of CPU
U = cpu utilization, 0 <= the U-<. 1 =
W is a task waiting time =
C = calculated time
T = Thread Pool Size
To achieve the desired processor utilization, thread the optimal size of the pool is equal to:
T = N*U*(1+W/C)

CPU number can be obtained by Runtime:

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

Of course, CPU cycles, is not the only resource influence the thread pool size, but also include memory, file handles, socket handles and database connections. These constraints on computing resources thread pool is easier: the resource demand is calculated for each task, and then using the resource Mateo; H divided by the total amount of each task demand, the obtained result is the thread pool size It limits.
When the task requires a resource by resource pools to manage, such as database connections, the size of the thread pool and pool resources will affect each other. If each task requires a database connection, then the connection pool size limits the size of the thread pool. Similarly, when the task thread pool is the only user of the database connection time, then the thread pool size in turn limit the size of the connection pool.

三、配置 ThreadPoolExecutor

ThreadPoolExecutor provides for a number of basic implementation Executor, which is returned by the Executor newCachedThreadPool, newFixedThreadPool newScheduledThreadExecutor factories and method of the Executors. ThreadPoolExecutor is a flexible, stable thread pool, allowing for a variety of custom.
If the Mo surface execution policy does not meet the requirements, it can be instantiated by ThreadPoolExecutor constructor of an object, and to customize according to their needs, and may refer to Executors the source to find the default execution policy in the configuration, then these execution policy be modified based. ThreadPoolExecutor defines a number of constructors.

public ThreadPoolExecutor{int corePoolSize,
        int maximumPoolSize, long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<RunnabXe> workQueue,
        ThreadFactory threadFactory,
        RejectedExecutionHandler handler) { ... }
  • 线程的创建与销毁

The basic thread pool size (Core Pool Size), the maximum size (Maximum Pool Size) and survival time were jointly responsible for thread creation and destruction. The basic size is the goal of the thread pool size that the thread pool size in the absence of tasks, and only in the work queue is full circumstances will exceed the number of threads created. The maximum size of the thread pool represents the maximum number of threads can be simultaneously active. If a thread is idle for more than the survival time, it will be marked as recyclable, and when the current size of the thread pool exceeds the basic size, this thread will be ended.
The basic size and survival time by adjusting the thread pool, thread pool can help reclaim resources idle threads possession, so that these resources can be used to perform other work. (Obviously, this is a compromise: the recovery of idle threads incur additional delay, because when demand increases, you must create a new thread to meet the needs)
newFixedThreadPool factory method will be basically the size of the thread pool and set the maximum size parameter specified value, and create a pool of threads do not time out. newCachedThreadPool factory method sets the maximum size of the thread pool is Integer.MAX_VALUE, and the basic size to zero, and set the timeout to 1 minute, out of this method to create a thread pool can be infinitely extended, and will reduce demand when automatic shrink. Other forms of thread pool can be configured by explicit ThreadPoolExecutor constructor.

  • 管理队列任务

Limited thread pool will limit the number of tasks that can be executed concurrently. (Single-threaded Executor is a notable exception: they can ensure that no tasks concurrently, as they thread safety is achieved by a thread closed.)
ThreadPoolExecutor allow a BlockingQueueto hold awaiting execution task. The basic task queuing methods are three kinds: unbounded queue, bounded queue and synchronous transfer (Synchronous Handoff) selected queue with other configuration parameters, such as the size of the thread pool and so on.
ewFixedThreadPool and newSingleThreadExecutor default will use an unbounded LinkedBlockingQueue If all worker threads are busy, then the task will be waiting in the queue. If the task is to reach sustained, rapid, and more than a thread pool their speed, the queue will increase indefinitely.
A more prudent resource management strategy is to use a bounded queue, for example ArrayBlockingQueuebounded LinkedBlockingQueue, PriorityBlockingQueuebounded queue helps avoid resource exhaustion occurs, but it brought a new problem: When the queue is full, new the task of how to do? When using bounded work queue, the queue size and the size of the thread pool must be adjusted together. If the thread pool is small and a large queue, then it helps to reduce memory usage, reducing CPU utilization, but also can reduce context switching, but the cost is likely to limit throughput.
For very large or unbounded thread pool, you can useSynchronousQueueTo avoid queuing task, and the task will be handed over directly from the producer to the worker thread. SynchronousQueue not really a queue, but a mechanism for handover between threads. To put an element SynchronousQueue, there must be another thread is waiting to accept this element. If no threads are waiting, and the current size of the thread pool is less than the maximum, then ThreadPoolExecutor will create a new thread, otherwise the saturation strategy, the task will be rejected. Direct transfer more efficient, because the task will be handed over directly to the implementation of its thread, instead of being first in the queue, the task is then extracted from the queue by the worker thread. Only when the thread pool is unbounded or may refuse the task, SynchronousQueue have real value. In newCachedThreadPool factory method on the use of SynchronousQueue.
When used as such ArrayBlockingQueue LinkedBlockingQueue or FIFO (First In First Out) queues, perform the same sequence of tasks to their arrival sequence. If you want more control task execution order, you can also use PriorityBlockingQueue, the queue to schedule tasks according to priority. Priority task is defined by a natural order or Comparator (if the task is achieved Comparable).
For the Executor, a good default choice when newCachedThreadPool factory method, he can provide better than a fixed-size thread pool queuing performance. When you need to limit the number of tasks in order to meet the current resource management needs, you can choose a fixed-size thread pool, as in network client server application to accept request, if not limited, so it is prone to overload.
Only when the tasks are independent or work queue for the thread pool limit set is reasonable. If there is a dependency between tasks, then bounded thread pool thread or queue it could lead to "Hunger" deadlock. At this point you should use unbounded thread pool, for example newCachedThreadPool.

  • 饱和策略

When the bounded queue to fill up, saturation policy comes into play. ThreadPoolExecutor的饱和策略可以通过调用seLRcjectedExecutionHandler来修改。(If a task that has been submitted to a closed Executor, saturation strategy will be used.) JDK provides several different RejectedExecutionHandler realize, every implementation is different saturation strategy includes: AbortPolicy, CallerRunsPolicy, DiscardPolicyand DiscardOldestPolicy.

  • “中止(Abort)”Saturation strategy is the default strategy, which will throw RejectedExecutionException unchecked. The caller can catch this exception and then write your own code to handle on demand. When the newly submitted task can not be saved in the queue waiting to execute that “抛弃(Discard)”strategy will quietly abandon the task. “抛弃最旧的( Discard-Oldest)”Strategies will be abandoned next task will be executed, and then try to re-submit a new task. (If the work queue is a priority queue, then the "most abandoned old" strategy will lead to abandon the highest priority task, it is best not to "abandon the oldest" saturation strategy and priority queues used together.)
  • “调用者运行 Caller-Runs)”Strategy to achieve a regulatory mechanism, the policy will neither abandon the task, it will not throw an exception, but will fall back to the caller certain tasks, thereby reducing the flow of new tasks. It does not submit new tasks in a thread pool thread, but in a call to perform the task execute threads. We can modify the example Webserver use bounded queues and "caller Run" saturation strategy, when all the threads in the pool are occupied, and the work queue is full, the next task when calling execute on the main thread in execution. Since the mission will take some time, so the main thread, at least not submit any task at a time, so that the worker thread has time to complete processing tasks being performed. During this period, the main thread does not call accept, request arrives and therefore will be saved in the queue TCP layer rather than in the application queue. The continuous overload, then the TCP layer will eventually find its request queue to fill up, so it will also start to abandon the request. When the server is overloaded, the overload situation will gradually spread outwardly open - TCP layer and then to the thread pool from the work queue to the application, the client ultimately, causing the server to implement a gradual reduction in the performance under a high load .
  • 线程工厂

Whenever you need to create a thread pool thread by thread factory method it is done. Default thread factory method will create a new, non-daemon threads, and does not contain specific configuration information. By specifying a thread factory method, you can customize the configuration of the thread pool. In ThreadFactoryonly defines a method newThread, whenever you need to create a new thread pool thread will call this method.

public interface ThreadFactory {
    Thread newThread(Runnable r);
}

However, in many cases require the use of a custom thread factory method. For example, you want to specify a UncaughtExceptionHandler for the thread pool thread, or instantiate a Thread class customized for executing recording debug information. You may also want to modify the priority of the thread (this is usually not a good idea) or guardian status (Again, this is not a good idea). Maybe you just want to thread a name more meaningful to explain the error log dump and thread.

public class MyThreadFactory implements ThreadFactory {

    @Override
    public Thread newThread(Runnable r) {
        Thread t = Executors.defaultThreadFactory().newThread(r);
        t.setDaemon(true);
        return t;
    }
}
  • 在调用构造函数后再定制ThreadPooExecutor

After calling ThreadPoolExecutor constructor, you can still be modified by setting function (Setter) Most of the parameters passed to its constructor (such as basic thread pool size, maximum size, survival time, a thread factory and rejected execution processor ( Rejected Execution Handler)). If Executor is created by a (except newSingleThreadExecutor) factory method in the Executors, you can convert the type of the result ThreadPoolExecutor to access the setter.

ExecutorService exec = Executors.newCachedThreadPool();
if (exec instanceof ThreadPoolExecutor){
    ((ThreadPoolExecutor) exec).setCorePoolSize(10);
}else{
    throw new AssertionError ("Oops, bad assumption");
}

Executors unconfigurableExecutorService contained in a plant, the method for packaging a conventional ExecutorService, it exposes only ExecutorService method, and therefore it can not be configured. newSingleThreadExecutor return this way the package ExecutorService, rather than the original ThreadPoolExecutoro although single-threaded Executor is actually implemented as a thread pool contains only a single thread, but it also ensures that the task will not be executed concurrently. If you increase the size of the thread pool of single-threaded Executor in the code, it will undermine its execution semantics.
You can use this technique in your own Executor to prevent the execution policy is modified. If ExecutorService exposed to untrusted code, do not want to modify it, it can be packaged by unconfigurableExecutorService.

四、扩展ThreadPoolExecutor

ThreadPoolExecutor is extensible, which provides several methods can be rewritten in subclassing: beforeExecute、 afterExecute和terminated,These methods can be extended with a dry behavior of ThreadPoolExecutor.
BeforeExecute afterExecute calls and other methods to perform tasks in the thread, in these methods can also add logging, timing, monitoring or statistics collection function. Whether the task is to run from the normal return, or throw an exception return, afterExecute will be called. (If the task with a Error Upon completion, it will not be called afterExecuteo) If beforeExecute throws a RuntimeException, the task will not be executed, and afterExecute will not be called.
After the call terminated when complete shut down operations in the thread pool, which is in all tasks have been completed and all worker threads have been shut down. terminated Executor can be used to release the resources allocated in its life cycle, in addition can also be performed to send notifications, logging, or finalize collect statistical information and other operations.

Guess you like

Origin blog.csdn.net/qq_27870421/article/details/90583181