参考:为什么使用线程池,参数解释、《Java 编程的逻辑》- 马俊昌
为什么用线程池
1、降低资源消耗:提高线程利用率,降低创建和销毁线程的消耗;
2、提高响应速度:任务来了,直接有线程可用可执行,而不用先创建线程,再执行;
3、提高线程的可管理性:线程是稀缺资源,使用线程池可以统一分配调优监控。
线程池参数
Java 并发包中线程池的实现类是 ThreadPoolExecutor,ThreadPoolExecutor 有一些重要的参数:
public class ThreadPoolExecutor extends AbstractExecutorService {
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// ...
}
}
1、int corePoolSize
代表核心线程数。线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了 allowCoreThreadTimeOut。这里的最小线程数量即是 corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了 corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务。
2、int maximumPoolSize
代表的是最大线程数。当前线程数达到 corePoolSize 后,如果继续有任务被提交到线程池,会将任务缓存到工作队列(后面会介绍)中。如果队列也已满,则会去创建一个新线程来出来这个处理。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由 maximumPoolSize 指定。
3、long keepAliveTime
表示空闲存活时间。一个线程如果处于空闲状态,并且当前的线程数量大于 corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由 keepAliveTime 来设定。
4、TimeUnit unit
表示 keepAliveTime 的时间单位。
5、BlockingQueue<Runnable> workQueue
表示工作队列,用来存放待执行的任务。JDK 中提供了四种工作队列:
-
ArrayBlockingQueue
基于数组的有界阻塞队列,按 FIFO 排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到 corePoolSize 后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到 maxPoolSize,则会执行拒绝策略。
-
LinkedBlockingQueue
基于链表的无界阻塞队列(其实最大容量为 Integer.MAX),按照 FIFO 排序。由于该队列的近似无界性,当线程池中线程数量达到 corePoolSize 后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到 maxPoolSize(很难达到 Integer.MAX 这个数),因此使用该工作队列时,参数 maxPoolSize 其实是不起作用的。
-
SynchronousQueue
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到 maxPoolSize,则执行拒绝策略。
-
PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数 Comparator 实现。
6、ThreadFactory threadFactory
线程工厂,用来生产线程执行任务。我们可以选择使用默认的创建工厂,产生的线程都在同一个组内,拥有相同的优先级,且都不是守护线程。当然我们也可以选择自定义线程工厂,用来设定线程名、是否为 daemon 线程等等。
7、RejectedExecutionHandler handler
任务拒绝策略。当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,JDK 中提供了 4 中拒绝策略:
-
CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的 run 方法,除非线程池已经 shutdown,则直接抛弃任务。
public static class CallerRunsPolicy implements RejectedExecutionHandler { /** * Creates a {@code CallerRunsPolicy}. */ public CallerRunsPolicy() { } /** * Executes task r in the caller's thread, unless the executor * has been shut down, in which case the task is discarded. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
-
AbortPolicy
该策略下,直接丢弃任务,并抛出 RejectedExecutionException 异常。
public static class AbortPolicy implements RejectedExecutionHandler { /** * Creates an {@code AbortPolicy}. */ public AbortPolicy() { } /** * Always throws RejectedExecutionException. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task * @throws RejectedExecutionException always */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
-
DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
public static class DiscardPolicy implements RejectedExecutionHandler { /** * Creates a {@code DiscardPolicy}. */ public DiscardPolicy() { } /** * Does nothing, which has the effect of discarding task r. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } }
-
DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列。
public static class DiscardOldestPolicy implements RejectedExecutionHandler { /** * Creates a {@code DiscardOldestPolicy} for the given executor. */ public DiscardOldestPolicy() { } /** * Obtains and ignores the next task that the executor * would otherwise execute, if one is immediately available, * and then retries execution of task r, unless the executor * is shut down, in which case task r is instead discarded. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }