合理地使用线程池能够带来3个好处:
- 降低资源消耗(通过重复利用已创建的线程降低线程创建和销毁造成的消耗)
- 提高响应速度(当任务到达时,任务可以不需要等到线程创建就能立即执行)
- 提高线程的可管理性(线程池可以进行统一分配、调优和监控线程)
当提交一个新任务到线程池时,线程池的处理流程如下:
ThreadPoolExecutor执行execute方法分下面4种情况:
corePoolSize
,则创建新线程来执行任务(需要获取全局锁)corePoolSize
,则将任务加入BlockingQueue
BlockingQueue
队列已满,则创建新的线程(非核心线程)来处理任务(需要获取全局锁)maximumPoolSize
,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()
方法什么是工作线程?线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,还会循环获取工作队列里的任务来执行
通过
new ThreadPoolExecutor(xxx)
,其中参数具体含义如下:
corePoolSize
(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务(即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建,allowCoreThreadTimeOut
方法可设置核心线程是否能被回收)runnableTaskQueue
(任务队列):用于保存等待执行的任务的阻塞队列(可选择Java并发容器和框架中2.2节介绍的队列)maximumPoolSize
(线程池最大数量):线程池允许创建的最大线程数(如果队列满了且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务)ThreadFactory
:设置创建线程的工厂RejectedExecutionHandler
(饱和策略):当队列和线程池都满了,则采取一种策略处理提交的新任务
AbortPolicy
(默认):直接抛出异常。CallerRunsPolicy
:只用调用者所在线程来运行任务。DiscardOldestPolicy
:丢弃队列里最近的一个任务,并执行当前任务DiscardPolicy
:不处理,丢弃掉且不抛出异常keepAliveTime
(线程活动保持时间):线程池的工作线程空闲后保持存活的时间(超过该时长,非核心线程就会被回收)TimeUnit
(线程活动保持时间的单位)可以使用两个方法向线程池提交任务:execute()和submit()
execute()
:用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功threadsPool.execute(new Runnable() {
@Override
public void run() {
...
}
})
submit()
:用于提交需要返回值的任务(返回future
类型的对象,该对象可以判断任务是否执行成功且可以通过对象的get()
来获取返回值)Future<Object> future = executor.submit(harReturnValuetask);
try {
Object s = future.get();
} catch (InterruptedException e) {
// 处理中断异常
} catch (ExecutionException e) {
// 处理无法执行任务异常
} finally {
// 关闭线程池
executor.shutdown();
}
可通过调用线程池的
shutdown
或shutdownNow
方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程
可以从以下几个角度配置线程池:
Ncpu+1
),因为CPU密集型任务使得CPU使用率很高,若开过多的线程数能增加上下文切换的次数,带来额外的开销2*Ncpu
),因为CPU使用率并不高,可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间PriorityBlockingQueue
来处理如果在系统中大量使用线程池,则需要对线程池进行监控,在出现问题时,可根据线程池的使用状况快速定位问题。可使用以下属性:
taskCount
:线程池需要执行的任务数量completedTaskCount
:线程池在运行过程中已完成的任务数量largestPoolSize
:线程池里曾经创建过的最大线程数量getPoolSize
:线程池的线程数量(线线程池不销毁的话,线程池里的线程不会自动销毁)getActiveCount
:获取活动的线程数beforeExecute
、afterExecute
和terminated
方法进行监控定长线程池(FixedThreadPool
):用于控制线程最大并发数
定时线程池(ScheduledThreadPool
):用于执行定时或周期性的任务
可缓存线程池(CachedThreadPool
):执行大量且耗时少的任务
单线程化线程池(SingleThreadExecutor
):应用于不适合并发但可能引起IO阻塞性及影响UI线程响应的操作,如数据库操作