1 线程池选型
比较重要的几个类:
| ExecutorService | 真正的线程池接口。 |
|---|---|
| ScheduledExecutorService | 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。 |
| ThreadPoolExecutor | ExecutorService的默认实现。 |
| ScheduledThreadPoolExecutor | 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。 |
1.1 newSingleThreadExecutor 创建一个单线程的线程池,如果这个线程因为异常结束,那么会创建一个新的线程来执行任务。 作用:保证所有任务按照提交的顺序来执行。 1.2 newFixedThreadPool 来一个请求就创建一个此线程,直到线程池的固定数量。但是他的任务队列是无界的。 1.3 newCachedThreadPool 创建一个可缓存的线程池,如果任务数量超过了当前最大线程数,那么就会回收(> 60s)的空闲线程,此线程池又可以智能的添加新线程来处理任务。 此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。 他的任务队列是SynchronousQueue。 简单说:该QUEUE中,每个插入操作必须等待另一个线程的对应移除操作。其实,也就是不保持任务,只要有任务来就创建线程来执行。
1.4 newScheduledThreadPool 创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
2 任务队列
-
FixedThreadPool(SingleThreadExecutor 同理)选取的是 LinkedBlockingQueue 他的线程数量是固定的,任务队列是无界队列。
-
CachedThreadPool 选取的是 SynchronousQueue 他的线程数量是无线的,不需要额外的空间来存储任务,因此每个任务都可以通过新建线程数进行执行。执行效率更高。
-
ScheduledThreadPool(SingleThreadScheduledExecutor同理)选取的是延迟队列
ScheduledThreadPool 处理的是基于时间而执行的 Task,而延迟队列有能力把 Task 按照执行时间的先后进行排序,这正是我们所需要的功能。
除了上述3种之外,还有一种最常用的是ArrayBlockingQueue。 常用于手动创建的线程池中,容量是有限且固定的。
3 拒绝策略
- 抛出一个拒绝异常:RejectedExecutionException。
- 直接忽略。
- 如果没有能力执行,那么就抛弃头节点的任务,通常是存活最长的任务。
- 如果没有能力执行,那么谁提交谁执行。
线程那些事
优点:线程的创建和销毁都需要系统调用,耗费资源。 缺点:占用内存,上下文切换耗费资源
1 为什么把任务放在队列中,而不是直接把最大线程池拉满。
核心线程数就是正常情况下运行的数量,最大线程数只是应付突发状况,所以,使用队列进行一个缓冲,减少线程创建和销毁,以及内存占用的代价。
2 线程池如何动态修改核心线程数和最大线程数?
我们可以很简单的去监控线程池的这些状态,然后给我们发送邮件以及报警等。 接着就是修改这些参数了:
可以搞个页面出来给研发人员使用。
设置核心线程数的过程:
首先判断是否小于0,小于则直接抛异常。
如果大于现有的,取任务队列中的任务数量和增加的线程数的最小值k,新增k个线程,每次新增后判断任务队列是否为空,如果为空,则停止新增。
如果小于现有的,则回收一些。