ThreadPoolExecutor 学习

来源:互联网 发布:网络运营者应当为 编辑:程序博客网 时间:2024/05/23 16:17
java.util.concurrent

Class ThreadPoolExecutor

  • java.lang.Object
    • java.util.concurrent.AbstractExecutorService
      • java.util.concurrent.ThreadPoolExecutor
  • All Implemented Interfaces:
    Executor, ExecutorService
  • Direct Known Subclasses:
    ScheduledThreadPoolExecutor

  • public class ThreadPoolExecutor extends AbstractExecutorService
  • An ExecutorService that executes each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods.
  • ExecutorService 会选择线程池中的某一个线程来执行提交上来的任务,通常使用Executors的工厂方法来配置 ExecutorService。
  • Thread pools address two different problems: they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks.
  • 线程池用来解决两个问题:1、执行大量的异步任务时,由于减少了每个任务创建时的开销,因此可以改善性能。2、可以管理多个并发任务执行过程当中,资源(包括Thread)的消耗。ThreadPoolExecutor 还维护一些统计项,比如已完成的任务数量。
  • To be useful across a wide range of contexts, this class provides many adjustable parameters and extensibility hooks. However, programmers are urged to use the more convenient Executors factory methodsExecutors.newCachedThreadPool() (unbounded thread pool, with automatic thread reclamation), Executors.newFixedThreadPool(int) (fixed size thread pool) and Executors.newSingleThreadExecutor() (single background thread), that preconfigure settings for the most common usage scenarios. Otherwise, use the following guide when manually configuring and tuning this class:
  • 为了适应复杂的使用场景,ThreadPoolExecutor 的构造函数有很多参数和hook。推荐使用更简单的 Executors 的工厂方法 Executors.newCachedThreadPool() (无限制大小的线程池,可自动回收线程) 、Executors.newFixedThreadPool(int) (固定大小的线程池)、Executors.newSingleThreadExecutor()(单线程)。Executors 的这些工厂方法封装了很多的复杂设置,从而更为易用。当然还可以按照下面的说明,直接使用ThreadPoolExecutor:
  • Core and maximum pool sizes
  • ThreadPoolExecutor will automatically adjust the pool size (see getPoolSize()) according to the bounds set by corePoolSize (see getCorePoolSize()) and maximumPoolSize (see getMaximumPoolSize()). When a new task is submitted in method execute(java.lang.Runnable), and fewer than corePoolSize threads are running, a new thread is created to handle the request, even if other worker threads are idle. If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full. By setting corePoolSize and maximumPoolSize the same, you create a fixed-size thread pool. By setting maximumPoolSize to an essentially unbounded value such as Integer.MAX_VALUE, you allow the pool to accommodate an arbitrary number of concurrent tasks. Most typically, core and maximum pool sizes are set only upon construction, but they may also be changed dynamically using setCorePoolSize(int) and setMaximumPoolSize(int).
    •      ThreadPoolExecutor 能够根据参数 corePoolSize 和 maximumPoolSize 自动调整线程池的大小。一个任务通过 ThreadPoolExecutor 对象的 execute 方法提交上来以后,可以分为以下几种情况:
    •       1、当池子里的线程数量小于corePoolSize时,会创建一个新的线程来处理,即使池子里的其他线程都处于空闲状态。
    •       2、当数量处于 corePoolSize 和 maximumPoolSize 之间时,仅当队列已经满了的时候才会创建新的线程。
    •       3、当corePoolSize==maximumPoolSize时,相当于创建了固定大小的线程池;当把参数maximumPoolSize设置为无限大(Integer.MAX_VALUE)时,相当于允许线程池产生任意多个线程。
    •       多数情况下,corePoolSize和maximumPoolSize都是在 ThreadPoolExecutor的构造函数中指定的,但是它们也可以在运行时通过函数 setCorePoolSize(int) 和 setMaximumPoolSize(int) 动态修改。
  • On-demand construction
    By default, even core threads are initially created and started only when new tasks arrive, but this can be overridden dynamically using method prestartCoreThread() or prestartAllCoreThreads(). You probably want to prestart threads if you construct the pool with a non-empty queue.
    •      当有任务提交上来时,core threads 默认会被创建和启动。但是这个行为可以通过覆盖 prestartCoreThread() 或者 prestartAllCoreThreads() 改写。这样就可以处理初始队列不是空的情况了。
  • Creating new threads
    New threads are created using a ThreadFactory. If not otherwise specified, a Executors.defaultThreadFactory() is used, that creates threads to all be in the same ThreadGroup and with the same NORM_PRIORITY priority and non-daemon status. By supplying a different ThreadFactory, you can alter the thread's name, thread group, priority, daemon status, etc. If a ThreadFactory fails to create a thread when asked by returning null from newThread, the executor will continue, but might not be able to execute any tasks. Threads should possess the "modifyThread" RuntimePermission. If worker threads or other threads using the pool do not possess this permission, service may be degraded: configuration changes may not take effect in a timely manner, and a shutdown pool may remain in a state in which termination is possible but not completed.
    •      ThreadPoolExecutor使用ThreadFactory创建新的线程。默认使用Executors.defaultThreadFactory方法,通过这个方法创建出的线程处于同一个ThreadGroup,拥有同样的线程优先级(NORM_PRIORITY),以及都被标记为
    •       non-daemon(非守护线程,未结束的non-daemon thread会导致JVM不能停止)。
  • Keep-alive times
    If the pool currently has more than corePoolSize threads, excess threads will be terminated if they have been idle for more than the keepAliveTime (see getKeepAliveTime(java.util.concurrent.TimeUnit)). This provides a means of reducing resource consumption when the pool is not being actively used. If the pool becomes more active later, new threads will be constructed. This parameter can also be changed dynamically using methodsetKeepAliveTime(long, java.util.concurrent.TimeUnit). Using a value of Long.MAX_VALUE TimeUnit.NANOSECONDS effectively disables idle threads from ever terminating prior to shut down. By default, the keep-alive policy applies only when there are more than corePoolSizeThreads. But method allowCoreThreadTimeOut(boolean) can be used to apply this time-out policy to core threads as well, so long as the keepAliveTime value is non-zero.
    •      如果池子里的线程数超过corePoolSize,那么多余的、处于空闲状态的线程将在 keepAliveTime 时间过后自动结束,这样可以降低线程池不使用时期的资源消耗。keepAliveTime 参数值能在运行时,通过函数
    •       setKeepAliveTime动态设置。将该参数值设为 Long.MAX_VALUE,相当于在线程池被关闭前,空闲线程永远无法结束。keepAliveTime 默认只影响超过 corePoolSize 数量的线程,但是可以通过函数
    •       allowCoreThreadTimeOut 来影响那些 core thread,只要keepAliveTime不等于 0。
  • Queuing
    Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing:
    •      任何 BlockingQueue 都可以用来传递和保存那些提交上来的任务。Queue的作用和池子大小有关系:
    • If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
    • 如果线程数量小于corePoolSize,那么首先创建新线程,而不是将任务加入队列。
    • If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
    • 如果大于等于corePoolSize,那么将任务加入队列而不会新建一个线程。
    • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
    • 如果任务无法入队:如果当前线程数未超过maximumPoolSize,那么新建一个线程;否则任务被拒绝。
    There are three general strategies for queuing:有3种队列策略
    • Direct handoffs. A good default choice for a work queue is a SynchronousQueue that hands off tasks to threads without otherwise holding them. Here, an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies. Direct handoffs generally require unbounded maximumPoolSizes to avoid rejection of new submitted tasks. This in turn admits the possibility of unbounded thread growth when commands continue to arrive on average faster than they can be processed.
    • 直接切换。使用SynchronousQueue将排队的任务直接交给一个线程处理,这是一个很好的方法。在这种情况下,如果不能立即创建一个线程处理一个任务,那么这个任务将无法入队。当处理一堆互有依赖的任务时,这一策略可以避免程序死机的情况。该策略需要将maximumPoolSize设为无限大,来保证随时都能将新任务入队。当一个接一个的任务到来而又来不及处理的时候这相当于允许创建无数个线程。
    • Unbounded queues. Using an unbounded queue (for example a LinkedBlockingQueue without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.) This may be appropriate when each task is completely independent of others, so tasks cannot affect each others execution; for example, in a web page server. While this style of queuing can be useful in smoothing out transient bursts of requests, it admits the possibility of unbounded work queue growth when commands continue to arrive on average faster than they can be processed.
    • 无限制容量的队列。使用没有设置容量的 LinkedBlockingQueue 的时候,如果所有 core thread 都不空闲,那么队列中的新任务将会一直处于等待状态。也就是说,永远不会有多于 corePoolSize 的线程存在(maximumPoolSize也就没有意义了)。这一策略适用于任务之间是完全独立的,运行时互不影响。比如网页服务器。这种入队方式有利于平滑处理激增的请求,它允许队列中存在无数个任务。
    • Bounded queues. A bounded queue (for example, an ArrayBlockingQueue) helps prevent resource exhaustion when used with finite maximumPoolSizes, but can be more difficult to tune and control. Queue sizes and maximum pool sizes may be traded off for each other: Using large queues and small pools minimizes CPU usage, OS resources, and context-switching overhead, but can lead to artificially low throughput. If tasks frequently block (for example if they are I/O bound), a system may be able to schedule time for more threads than you otherwise allow. Use of small queues generally requires larger pool sizes, which keeps CPUs busier but may encounter unacceptable scheduling overhead, which also decreases throughput.
    • 限制容量的队列。ArrayBlockingQueue 和 maximumPoolSize 一起使用来防止资源枯竭,但是比较难以权衡 maximumPoolSize 以及队列大小:大队列小池子降低了CPU的使用率、系统资源消耗,以及线程间的切换成本,但是同时人为地导致了低吞吐量。如果任务经常被阻塞(比如经常I/O操作),其实系统原本可以为更多的线程分配执行时间。反过来说,使用小的队列和大的池子能保持较高的CPU使用率,但是可能遇到无法接受的资源消耗成本,这样同样会降低吞吐量。
    Rejected tasks
    New tasks submitted in method execute(java.lang.Runnable) will be rejected when the Executor has been shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is saturated. In either case, the execute method invokes the RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) method of its RejectedExecutionHandler. Four predefined handler policies are provided:
    •      遇到以下情况,任务会被拒绝:1、Executor被关闭。2、Executor使用了Bounded queue,达到了maximumPoolSize并且queue是满的。无论哪种情况,execute函数都会回调 RejectedExecutionHandler的   
    •      rejectedExecution 函数。有以下4种RejectedExecutionHandler的子类
    • In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection.
    • 只抛出异常。
    • In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted.
    • 提供了一种简单的拒绝任务的方式:将任务交由调用线程处理。降低向ThreadPoolExecutor提交任务的频率。
    • In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped.
    • 抛弃任务。
    • In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.)
    • 在 Executor 没有关闭的情况下,队首的任务会被丢弃,然后重试执行刚刚被提交的任务(可能再次失败,然后再次重试)。
    It is possible to define and use other kinds of RejectedExecutionHandler classes. Doing so requires some care especially when policies are designed to work only under particular capacity or queuing policies.
    •    可以自定义或者使用其他的 RejectedExecutionHandler 的子类。需要特别注意的是,某些 RejectedExecutionHandler 是专门设计用于特定的排队策略或者队列容量。
  • Hook methods
    This class provides protected overridable beforeExecute(java.lang.Thread, java.lang.Runnable) and afterExecute(java.lang.Runnable, java.lang.Throwable) methods that are called before and after execution of each task. These can be used to manipulate the execution environment; for example, reinitializing ThreadLocals, gathering statistics, or adding log entries. Additionally, method terminated() can be overridden to perform any special processing that needs to be done once the Executor has fully terminated.
    If hook or callback methods throw exceptions, internal worker threads may in turn fail and abruptly terminate.
  • 如果hook或者回调函数抛出异常,那么内部的工作线程可能会意外终止。

    Queue maintenance
    Method getQueue() allows access to the work queue for purposes of monitoring and debugging. Use of this method for any other purpose is strongly discouraged. Two supplied methods, remove(java.lang.Runnable) and purge()are available to assist in storage reclamation when large numbers of queued tasks become cancelled.
    •      getQueue() 函数可以用于观察和debug work queue,强烈建议不要做其他操作。另外两个函数 remove 和 purge 用于当大量的任务被取消时,回收存储空间。
  • Finalization
    A pool that is no longer referenced in a program AND has no remaining threads will be shutdown automatically. If you would like to ensure that unreferenced pools are reclaimed even if users forget to call shutdown(), then you must arrange that unused threads eventually die, by setting appropriate keep-alive times, using a lower bound of zero core threads and/or setting allowCoreThreadTimeOut(boolean).
    •     ThreadPoolExecutor不再被引用,并且不包含任何线程的情况下,它将被自动关闭。如果希望即使用户忘记调用shutdown函数的情况下,一个不再被引用的ThreadPoolExecutor能够被自动关闭,那么你必须    
    •      保证它里面的所有线程最终都会结束。可以设置合适的 keepAliveTime,也可以将corePoolSize设为0,还可以调用 allowCoreThreadTimeOut 函数。
  • Extension example. Most extensions of this class override one or more of the protected hook methods. For example, here is a subclass that adds a simple pause/resume feature:

     class PausableThreadPoolExecutor extends ThreadPoolExecutor {   private boolean isPaused;   private ReentrantLock pauseLock = new ReentrantLock();   private Condition unpaused = pauseLock.newCondition();   public PausableThreadPoolExecutor(...) { super(...); }   protected void beforeExecute(Thread t, Runnable r) {     super.beforeExecute(t, r);     pauseLock.lock();     try {       while (isPaused) unpaused.await();     } catch (InterruptedException ie) {       t.interrupt();     } finally {       pauseLock.unlock();     }   }   public void pause() {     pauseLock.lock();     try {       isPaused = true;     } finally {       pauseLock.unlock();     }   }   public void resume() {     pauseLock.lock();     try {       isPaused = false;       unpaused.signalAll();     } finally {       pauseLock.unlock();     }   } }
  • Since:
    1.5
0 0
原创粉丝点击