什么是线程池?
线程池是一种用于管理和复用线程的机制,旨在减少线程的创建和销毁次数,提高线程的可重用性和执行效率。通过线程池,可以控制线程的数量、数量大小以及线程的执行方式,从而更加有效地处理并发任务。
线程池的好处?
- 降低系统开销:减少了线程的创建和销毁次数,减少了系统资源的开销。
- 提高响应速度:线程池中的线程预先创建好,可以立即执行任务,提高了系统的响应速度。
- 精细的调优:可以通过配置线程池的参数,如核心线程数、最大线程数、任务队列等,来灵活地控制线程执行的规模和调度方式。
常用的线程池?
1、FixedThreadPool(固定大小线程池):
- 固定大小的线程池,线程数量一旦达到设定的核心线程数,就保持不变。
- 如果有任务提交时,如果空闲线程可以处理,则立即处理;如果没有空闲线程,则任务进入队列等待。
- 适用于需要限制线程数量的场景,比如数据库连接池等。
2、CachedThreadPool(缓存线程池):
- 线程数量根据需要自动调整,没有核心线程,最大线程数为 Integer.MAX_VALUE。
- 如果有空闲线程可用,则复用空闲线程;没有则动态创建新线程。
- 适用于执行大量短期异步任务的场景。
3、SingleThreadPool(单线程池):
- 只有一个线程在工作,保证所有任务按照指定顺序执行。
- 如果这个线程异常结束,会创建一个新的线程代替它,保证顺序执行。
- 适用于需要顺序执行任务的场景。
4、ScheduledThreadPool(定时任务线程池):
- 用于执行定时任务和周期性任务。
- 可以指定核心线程数量,可以根据需要动态调整线程数量。
线程池的七个参数?
1、核心线程数(corePoolSize):
- 核心线程数指的是线程池中保持存活的线程数量,即使线程处于空闲状态也会保持存活,除非设置了允许核心线程超时。
2、最大线程数(maximumPoolSize):
- 最大线程数表示线程池中最大允许存在的线程数量,当任务达到一定数量时,线程池会创建新的线程来处理任务,直到达到最大线程数为止。
3、线程空闲时间(keepAliveTime):
- 线程空闲时间表示当线程处于空闲状态时,等待新任务的最长时间,超过这个时间会被回收销毁,只保留核心线程数的线程。也称为线程的存活时间。
4、时间单位(unit):
- 时间单位用来指定 keepAliveTime 的单位,可以是秒、毫秒等。
5、工作队列(workQueue):
- 工作队列用来存储线程池等待执行的任务,有不同的实现方式,如 LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue 等。
6、线程工厂(threadFactory):
- 线程工厂用来创建新线程,可以自定义线程工厂来为线程池创建定制化的线程。
7、拒绝策略(handler):
拒绝策略用来指定当任务无法被线程池执行时的处理方式,有多种预定义的策略可供选择,默认是 AbortPolicy。一共有四种拒绝策略:
- AbortPolicy(中止策略):
默认的拒绝策略。当线程池无法接受新任务时,会抛出 RejectedExecutionException 异常通知调用者任务无法继续提交。
- CallerRunsPolicy(调用者运行策略):
如果线程池无法接受新任务,则由主线程(提交任务的线程)自己执行这个任务。这种策略不会抛弃任务,而是直接在主线程中执行。
- DiscardPolicy(丢弃策略):
当线程池无法接受新任务时,不做任何处理,直接丢弃无法执行的任务。
- DiscardOldestPolicy(丢弃最老的任务策略):
当线程池无法接受新任务时,丢弃最早加入工作队列但尚未被执行的任务,然后尝试将新任务添加到队列中。
如何创建线程池?
1、手动创建 ThreadPoolExecutor 对象:
- 可以通过直接创建 ThreadPoolExecutor 对象来自定义线程池的各种参数,如核心线程数、最大线程数、工作队列类型等。
- 通过构造函数 new ThreadPoolExecutor() 可以自定义线程池的各项参数,实现灵活的线程池管理。
2、Executors 工厂方法:
- Executors 类提供了一系列静态工厂方法快速创建常见类型的线程池,如 newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor 等。
- 这些工厂方法可以方便地创建不同类型的线程池,简化了线程池的创建过程,但有时可能会因为一些默认参数而不适用于特定场景。
3、ScheduledExecutorService 创建定时任务线程池:
- ScheduledExecutorService 是一个支持定时任务和周期性任务执行的线程池。通过 Executors.newScheduledThreadPool() 方法创建。
- 可以执行定时任务和周期性任务,具有方便的定时执行功能。
4、ForkJoinPool 创建并行任务线程池:
- ForkJoinPool 是 Java 7 中引入的用于支持并行任务执行的线程池,适用于拆分任务并行执行的场景。
- 通过 new ForkJoinPool() 构造方法创建,并通过 ForkJoinTask 提交任务。
线程池的执行流程?
为什么阿里的Java开发规范中禁止使用Executors创建线程池?
1、FixedThreadPool和SingleThreadPool
这两种线程池都使用无界的 LinkedBlockingQueue 作为任务队列,当请求队列长度无限制时(Integer.MAX_VALUE),如果请求产生过快,可能会导致请求堆积,消耗大量内存资源,最终导致内存溢出。
2、CachedThreadPool和ScheduledThreadPool
这两种线程池使用的是 SynchronousQueue,且线程数量上限为 Integer.MAX_VALUE。如果系统中的任务量过大,可能会导致大量线程被创建,这会消耗大量系统资源,包括内存和 CPU 资源,最终也可能导致内存溢出。
3、线程池的拒绝策略可能不当
Executors创建的线程池默认的拒绝策略是抛出RejectedExecutionException异常,这可能会导致任务提交者无法得到合理的反馈,而采用更合理的拒绝策略需要自定义ThreadPoolExecutor。
ps:以下是我整理的java面试资料,密码是obht,感兴趣的可以看看。最后,创作不易,觉得写得不错的可以点点关注!
链接:https://www.yuque.com/u39298356/uu4hxh?# 《Java面试宝典》