目录
1.什么是线程池?
都知道在java中有线程,但是创建一个线程需要上下文切换,避免频繁地创建和销毁线程其实是非常消耗性能的,线程池能帮助开发者创建线程,提高开发效率,能控制线程的数量。
总结有如下:
-
提高性能和效率:通过线程池,可以避免频繁地创建和销毁线程,从而减少了线程创建和上下文切换的开销,提高了系统的性能和效率。
-
控制并发度:线程池可以限制同时执行的线程数量,通过设置线程池的大小和队列容量,可以控制系统的并发度,防止因过多的线程导致系统资源耗尽或性能下降。
-
提供线程管理和调度:线程池可以统一管理和调度线程的生命周期,包括创建、执行和销毁线程等操作。它可以有效地分配任务给空闲线程,并根据需要动态调整线程数目。
-
支持异步执行:通过线程池,可以将任务提交给线程池进行异步执行,不需要等待任务执行完成,可以继续执行其他操作。这在处理大量耗时任务时特别有用,可以提高系统的响应速度和吞吐量。
-
提供线程池监控和统计:线程池通常提供监控和统计功能,可以获取线程池的运行状态、线程活动数量、任务完成情况等信息,有助于了解系统的运行状况,并进行性能调优和故障排查。
2.线程池有哪些创建方式?
jdk提供了四种创建方式如下:
newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回 收空闲线程,若无可回收,则新建线程。
newFixedThreadPool:创建固定线程数量,一旦超出会在队列中等待。
newScheduledThreadPool:创建定时线程,支持周期性支持线程。
newSingleTheradExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任 务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
但是阿里开发手册上明确说明不用这四种方法,至于为什么呢?往后看下去
可以使用ThreadPoolExecutor
来自定义线程池,可自行设置线程池参数
3.线程池的参数
在线程池中一共有7个核心参数:
-
corePoolSize 核心线程数目 - 池中会保留的最多线程数
-
maximumPoolSize 最大线程数目 - 核心线程+救急线程的最大数目
-
keepAliveTime 生存时间 - 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
-
unit 时间单位 - 救急线程的生存时间单位,如秒、毫秒等
-
workQueue - 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
-
threadFactory 线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
-
handler 拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略
有用4中拒绝策略:
第一种是直接报错,第二种是由主线程来执行任务,第三种是取出最开始的线程,第四种是丢弃当前线程,默认是抛异常
4.线程池的队列有哪些呢?
数组阻塞队列(ArrayBlockingQueue):一个基于数组的有界队列,需要指定最大容量。当队列已满时,新任务将等待直到队列有空闲位置
链表阻塞队列(LinkedBlockingQueue):一个基于链表的无界队列,可以存储任意数量的任务。当任务提交速度大于处理速度时,队列会不断增长。虽然可以用作无界队列,但在初始化时指定容量后,也可以成为有界队列。
优先队列(PriorityBlockingQueue):基于优先级堆的无界优先级队列,每次取出元素都是队列中优先级最高的任务。
延迟队列(Delayed Queue):一个支持延迟获取元素的无界阻塞队列。只有延迟期满的元素才能从队列中取出.
同步队列(SynchronousQueue):一个没有存储元素的队列。每个插入操作必须等待另一个线程进行移除操作,反之亦然。主要用于生产者和消费者线程间的直接传递任务。
5.为什么不建议用Executor来创建线程池?
看完3和4多多少少应该有点察觉了吧,其实就是因为参数的原因,
主要有工作队列和最大线程数这两个参数影响,
一旦工作队列的线程数一直进入新的线程,这时候的内存就会bug,报错OOM
而最大线程数如果一直创建新的线程也是会OOM
6.线程池的执行流程
当一个线程进来时,
第一会查看核心线程是否有位置,有位置就添加进去
没有位置就会查看工作队列中是否满了,没满就进去
满了的情况下看看正在执行的线程有没有达到最大线程数,没有就创建一个新的线程
最大线程数也满了的情况下,这时候就要由我们的拒绝策略登场了,根据不同的拒绝策略来对这个线程进行最后的审判;
7.线程参数如何设置
分为CPU密集型和IO密集型
CPU密集型:cpu核数+1
IO密集型:CPU核数*2
好了线程池的一些问题就这些,后续的问题俺会持续更新=。=