Java线程池详解
线程池解释
线程池采用了池化思想,能够有效的管理线程的生命周期,减少了每次获取资源的消耗,提高了资源的利用率。类似池化实现还有数据库连接池、HTTP连接池等
好处
- 减少了线程创建和销毁的开销
- 提高了响应速度
- 使得线程更加方便管理
常见使用场景
- 量大处理时间较短的任务:有效利用线程池减少线程创建销毁的消耗
- 需要限制线程数量时:线程越多对资源的消耗越大,线程池可以设置最大线程数进行限制
- 异步执行一批不需要立刻反馈结果的任务:异步执行任务,减少系统响应时间
线程池的引入:Executor框架
Executor框架为Java 5 引入,将传统线程的操控方法进行优化升级,使其针对不同 场景更加的灵活、管理更加的方便,其核心jar包为
java.util.concurrent
,简称JUC
。此外,很重要的一点,该框架还避免了this逃逸
问题。this逃逸:指在构造函数返回之前,其他线程持有了该对象的引用,此时引用该对象调用其方法时,可能会出现不好排查的异常。
Executor框架主要包括三个部分
-
任务:
Runnabale/Callable
,可以被ThreadPoolExecutor
或ScheduledThreadPoolExecutor(继承ThreadPoolExecutor)
执行 -
执行:通过实现
Executor
接口的子接口ExecutorService
去构造相对来说比较完整的线程池执行系统 -
返回值:线程池的优势之一,通过实现
Futrure
接口的FutureTask
类将异步执行的结果获取到 -
主线程创建
Runnable
或者Callable
的任务对象,然后把 实现的Runnbale
或者Callable
交给ExecutorService
执行:ExecutorService.execute(Runnable command)
或者 把Runnable
对象或者Callable
对象提交给ExecutorService
执行(ExecutorService.submit(Runnable command)
) -
如果执行
ExecutorService.submit()
,ExecutorService
返回一个实现了Future
接口的对象(submit()
会返回一个FuturesTask
对象,FutureTask
实现了Runnable
,可以创建FutureTask
,然后直接交给ExecutorService
执行;execute
则会把异常打印出来) -
最后,主线程执行
FuturesTask.get()
方法等待任务执行完成。也可以通过FutureTask.cancel(boolean mayInterruptIfRunning)
来取消此任务执行。
以
ThreadPoolExecutor
为例,ThreadPoolExecutor
继承了AbstractExecutorService
(抽象函数)的submit方法,并且实现了AbstractExecutorService
来自Executor
接口的execute
方法,因此在调用线程池的submit
方法时就会通过ThreadPoolExecutor
的execute
将任务(Runnabale/Callable
为核心的RunnableFuture
对象)加到工作队列中(addWorker
方法实现),返回值则通过RunnableFuture
对象的形式返回。
简单来说就是主线程将Runnabale/Callable
对象通过submit
方法提交给线程池,线程池通过内部调度按照不同的策略执行多线程任务,然后通过返回的Futrure
对象的get
方法取出执行结果。
ThreadPoolExecutor 类
线程池实现类
ThreadPoolExecutor
是Executor
框架最核心的类
主线程提交任务后
- 第一步,判断核心线程池线程数是否已满,若未满则创建线程,若已满则进入第二步
- 第二步,判断等待队列是否已满,若未满则加入队列,若已满则进入第三步
- 第三步,判断最大线程数是否已满,若未满则创建线程,若已满则根据设定的拒绝策略处理
参数介绍:
corePoolSize
:核心线程数,能第一时间处理的线程数maximumPoolSize
:最大线程数,当核心线程池以及队列都满了的时候,线程池就会扩充到最大线程数workQueue
: 当核心线程池已满时,队列可以用来暂存后边进来的任务keepAliveTime
:线程数量大于核心线程数量时,线程闲置时间超过此值的线程会被回收掉,直至缩到核心线程数unit
:keepAliveTime
参数的时间单位threadFactory
:线程工厂,executor
创建线程时用到handler
:拒绝策略线程池已达到最大线程数,此时队列也满了,再有任务进来就会触发拒绝策略
拒绝策略:
AbortPolicy
:拒绝任务并抛异常RejectedExecutionException
CallerRunsPolicy
:直接在调用线程中执行任务,影响性能DiscardPolicy
:拒绝任务,不报异常,不做任何处理DiscradOldestPolicy
:拒绝掉最早的任务,执行最新的
线程池相关的内容有很多,该文章重在介绍,若要深究其中的某个元素,可以通过参考源码的方式,这样可以最直观的看到内部组成以及各个元素之间如何合作解决线程池的实现问题。