分析&回答
为什么使用线程池
- 降低资源消耗。重复利用已创建线程,降低线程创建与销毁的资源消耗。(尤其是当程序中需要创建大量生存期很短暂的线程时)
- 提高响应效率。任务到达时,不需等待创建线程就能立即执行。
- 提高线程可管理性。
- 防止服务器过载。内存溢出、CPU耗尽
原理
线程池和数据库连接池有点类似的是,线程池在系统启动时创建大量空闲线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该线程对象的run方法,当run方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲线程,等待执行下一个Runnable对象的run方法。
优点
使用线程池可以有效的控制系统中并发线程的数量,当系统中包含大量的并发线程时,会导致系统性能剧烈下降,甚至导致JVM的崩溃,而线程池的最大线程参数可以控制系统中并发线程数目不超过此数目。
线程池种类
- ExecutorService代表尽快执行线程的线程池(只要线程中有空闲的线程就立即执行线程任务)。
- ScheduledExecutorService代表可在指定延迟或周期性执行线程任务的线程池。
种类 | 实现 | 基于的阻塞队列 | 说明 | 类型 |
---|---|---|---|---|
固定大小线程池 | FixedThreadPool | LinkedBlockingQueue | 固定线程数量,任务队列无界 | ExecutorService |
交付模式线程池 | CachedThreadPool | SynchronousQueue | 线程数量不限制,任务队列不存放数据 | ExecutorService |
并行线程池 | WorkStealingPool | FIFO_QUEUE或LIFO_QUEUE | 线程数量不限制,分割任务执行 | ExecutorService |
定时任务线程池 | ScheduledThreadPool | DelayedWorkQueue | 线程数量不限制,任务按时间优先级执行 | ScheduledExecutorService |
单例固定线程池 | SingleThreadExecutor | LinkedBlockingQueue | 线程数量1个,任务按加入顺序执行 | ExecutorService |
ThreadPoolExecutor线程池参数
- corePoolSize - 池中所保存的线程数,包括空闲线程。
- maximumPoolSize - 池中允许的最大线程数。
- keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
- unit - keepAliveTime 参数的时间单位。
- workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
- threadFactory - 执行程序创建新线程时使用的工厂。
- handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
线程池的主要处理流程
1.在创建了线程池之后,等待提交过来的任务请求
2.当调用execute()方法添加一个请求任务的时候,线程池会做出如下判断:
2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个程序
2.2 如果正在运行的线程数量大于或者等于corePoolSize,那么将这个任务放入队列
2.3 如果这个时候队列满了并且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻执行这个任务
2.4 如果队列满了并且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行
3. 当一个线程完成任务的时候,它会从队列中取下一个任务来执行
4. 当一个线程无事可做超过一定时间(keepAliveTime)时:线程会判断如果当前运行的线程数
大于corePoolSize,那么这个线程就会被停掉。
阻塞队列
队列 | 实现 | 说明 |
---|---|---|
LinkedBlockingQueue | 基于链表和显示锁实现的队列 | 类似于LinkedList支持并发 |
SynchronousQueue | 基于双栈/双队列和显示锁实现的交付队列 | 基于双栈/双队列算法 |
DelayedWorkQueue | 基于优先级队列和显示锁实现的延迟队列 | 与优先级队列类似,由二叉堆算法实现 |
反思&扩展
线程池有哪些拒绝策略
- AbortPolicy:中止策略。默认的拒绝策略,直接抛出 RejectedExecutionException。调用者可以捕获这个异常,然后根据需求编写自己的处理代码。
- CallerRunsPolicy:调用者运行策略。在调用者线程中执行该任务。该策略实现了一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将任务回退到调用者(调用线程池执行任务的主线程),由于执行任务需要一定时间,因此主线程至少在一段时间内不能提交任务,从而使得线程池有时间来处理完正在执行的任务。
- DiscardOldestPolicy:抛弃最老策略。抛弃阻塞队列中最老的任务,相当于就是队列中下一个将要被执行的任务,然后重新提交被拒绝的任务。如果阻塞队列是一个优先队列,那么“抛弃最旧的”策略将导致抛弃优先级最高的任务,因此最好不要将该策略和优先级队列放在一起使用。
- DiscardPolicy:抛弃策略。什么都不做,直接抛弃被拒绝的任务。
策略 | 说明 |
---|---|
AbortPolicy | 当任务添加到线程池中被拒绝时,它将抛出RejectedExecutionException 异常。 |
CallerRunsPolicy | 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。 |
DiscardOldestPolicy | 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。 |
DiscardPolicy | 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。 |
喵呜面试助手: 一站式解决面试问题,你可以搜索微信小程序 [喵呜面试助手] 或关注 [喵呜刷题] -> 面试助手 免费刷题。如有好的面试知识或技巧期待您的共享!