前言
线程池的产生是为了管理线程,线程的频繁创建或不限制的创建会对系统带来相当大的资源损耗。在JAVA1.5中,提供了Executor框架用于将任务的提交和执行解耦,任务提交由Runnable或Callable处理,而Executor框架来处理任务。
ThreadPoolExecutor
ThreadPoolExecutor是线程池的核心实现类。其构造方法有4个,我们选最多参数的构造方法讲解:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:核心线程数。如果当前运行的线程数小于corePoolSize,则创建新的线程来处理任务;如果大于corePoolSize,则不会再继续创建。如果调用prestartAllcoreThread方法,线程池会提前创建并启动所有核心线程来等待任务;否则,默认情况下,只有提交了任务才会创建线程。
maximumPoolSize:线程池允许创建的最大线程数。如果任务队列满了并且线程池中线程数小于maximumPoolSize,则会仍旧会创建线程来进行处理任务,此时创建的线程称为非核心线程,此时创建的线程存活时间会受keepAliveTime约束。
keepAliveTime:非核心线程闲置时间。线程闲置超过这个时间会被回收,所以如果任务比较多,且每个任务执行用时比较短时,可以考虑增大keepAliveTime值来提高利用率。值得一提是,如果通过设置allowCoreThreadTimeOut(true),keepAliveTime也会应用到核心线程。
unit:keepAliveTime的时间单位。可选参数为:天(DAYS)、小时(HOURS)、分钟(MINUTES)、秒(SECONDS)、毫秒(MILLISECONDS)等。
workQueue:任务队列。如果当前线程大于corePoolSize,则将任务添加至此任务队列中,类型为BlockingQueue,是一个阻塞队列。
threadFactory:线程工厂。可以用线程工厂给每个创建出来的线程设置名字。
RejectedExecutionHandler:饱和策略。当任务队列和线程池都满了时所采取的应对策略。有如下4中策略:
- AbordPolicy:默认值,表示无法处理新任务,并抛出
- CallerRunsPolicy:用调用者所在的线程处理任务。
- DiscardPolicy:不能执行的任务,并将该任务删除。
- DiscardOldestPolicy:丢弃队列最近的任务,并执行当前任务。
线程池的工作流程

图中已经表现的非常清楚,便不再多说,只是顺带一提的是,如果任务队列中有任务在等待执行的话,核心线程执行完任务后会从任务队列中取任务进行执行,直至队列为空。
四种常用线程池
这四种常用线程池,其实无非是对ThreadPoolExecutor的参数进行不同的配置而产生,他们分别为:FixedThreadPool、CachedThreadPool、SingleThreadPool、ScheduledThreadPool。
1、FixedThreadPool
在Executors类的newFixedThreadPool方法中,可以看到如下一段代码,这段代码便是创建FixedThreadPool线程池。从参数可知,核心线程数和线程池线程总数均为nThreads,即此线程池无非核心线程,采用的阻塞队列为无界阻塞队列。
ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());

2、CachedThreadPool
在Executors类中的newCachedThreadPool方法中,可看到CachedThreadPool的构造参数。该线程池无核心线程,均为非核心线程,线程数量为Integer.MAX_VALUE,即无上限,空闲等待时间为60秒。阻塞队列用了不存储元素的SynchronousQueue,每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。此线程池比较适用于大量的需要立即处理的但耗时比较少的任务。
ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());

3、SingleThreadExecutor
在Executors类中的newSingleThreadExecutor方法中,可看到SingleThreadExecutor的构造参数。该线程池只有一个核心线程,无非核心线程。阻塞队列为无界阻塞队列。
new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));

4、ScheduledThreadPool
ScheduledThreadPool是一个能实现定时和周期性任务的线程池,在Executors中的ScheduledExecutorService方法中,创建源码如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
源码中创建的ScheduledThreadPoolExecutor类是继承自ThreadPoolExecutor,用于给定时处理任务。ScheduledThreadPoolExecutor构造函数如下,可以看出核心线程数由入参决定,非核心线程数为无限,但采用阻塞队列DelayedWorkQueue是无界,所以无非核心线程。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
与前三种线程池有些不同的是,当执行ScheduledThreadPoolExecutor的scheduleAtFixedRate或scheduleWithFixedDelay方法时,会向DelayedWorkQueue添加一个实现RunnableScheduleFuture接口的ScheduleFutureTask,并检查运行的线程数量是否已达到corePoolSize。如果还未达到,则创建新的线程并启动它,但不会立即去执行任务,而是去DelayedWorkQueue中取ScheduleFutureTask,然后执行任务。如果运行的线程数已经达到corePoolSize,则将任务添加到DelayedWorkQueue中。DelayedWorkQueue会将任务进行排序,先执行的任务放在队列前面。最重要的是,执行完的任务,会将ScheduleFutureTask中的time变量改为下次执行的时间放回到DelayedWorkQueue中,以此达到循环定时的功能。

网友评论