Java面试高频问题汇总 线程池专题
时间:2022-07-25
本文章向大家介绍Java面试高频问题汇总 线程池专题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
池化技术是一种广泛应用的技术。线程池、数据库连接池、Http连接池都是这种技术的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
线程池提供了一种限制和管理资源(包括执行一个任务)。每个线程池还维护一些基本统计信息,例如已完成的任务数量。
使用线程池可以带来以下好处:
- 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
Runnable接口和Callable接口的区别
-
Runnable
自Java 1.0以来一直存在,但Callable
仅在Java 1.5中引入,目的就是为了来处理Runnable
不支持的用例。 -
Runnable
接口不会返回结果或者抛出检查异常,但是Callable
接口可以。
工具类Executors
可以实现Runnable
对象和Callable
对象的相互转换。
execute()方法和submit()方法的区别
execute()
方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否。
submit()
方法用于提交需要返回值的任务,线程池会返回一个Future
类型的对象,通过这个Future
对象可以判断任务是否执行成功。可以通过Future
的get()
方法来获取返回值,get()
方法会阻塞当前线程直到任务完成,而get(long timeout, TimeUnit unit)
方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
线程池如何创建
《阿里巴巴Java开发手册》中强制线程池不允许使用Executors
去创建,而是通过ThreadPoolExecutor
的方式,这中强制要求的目的在于让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors
返回线程池对象的弊端如下:
-
FixedThreadPool
和SingleThreadExecutor
:允许请求的队列长度为Integer.MAX_VALUE
,可能会堆积大量的请求,从而导致OOM
。 -
CacheThreadPool
和ScheduledThreadPool
:允许请求的线程数量为Integer.MAX_VALUE
,可能会创建大量线程,从而导致OOM
。
方法一:通过ThreadPoolExecutor
构造方法实现
ThreadPoolExecutor
ThreadPoolExecutor(int , int, long, TimeUnit, BlockingQueue<Runnable>)
ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFacory)
ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, RejectedExecutionHandler)
ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFctory, RejectedExecutionHandler)
方法二:通过Executor框架的工具类Executors
来实现
该方法可以创建四种类型的ThreadPoolExecutor
:
-
FixedThreadPool
:该方法返回固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。 -
SingleThreadExecutor
:方法返回仅有一个线程的线程池。若有多于一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。 -
CachedThreadPool
:该方法返回一个可根据实际情况调整线程数量的线程池,线程数量不确定,若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。 -
ScheduledThreadPool
:方法返回一个能实现定时、周期性任务的线程池。主要用于给定延时之后的运行任务或者定期处理任务。
以上四个类对应的Executor
全部通过调用父类ThreadPoolExecutor
的构造方法来实现
ThreadPoolExecutor构造函数分析(重要)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFctory threadFactory, RejectedExecutionHandler handler)
ThreadPoolExecutor3个最重要的参数:
-
corePoolSize
:核心线程数,定义了最小可以同时运行的线程数。 -
maximumPoolSize
:当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。 -
workQueue
:当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。
ThreadPoolExecutor其他常见参数
-
keepAliveTime
:当线程池中的线程数量大于corePoolSize
的时候,如果没有新任务提交,核心线程之外的线程并不会被立即销毁,而是会等待,等待时间超过了keepAliveTime
才会被回收销毁。 -
unit
:指定keepAliveTime
参数的时间单位。 -
threadFactory
:executor创建新线程时候会用到。 -
handler
:饱和策略,当线程池达到饱和状态时,采用的策略。
ThreadPool饱和策略
如果当前同时运行的线程数量达到最大线程数量并且队列队列也已经被放满了任务时,可以使用ThreadPoolTaskExecutor
定义的一些策略:
-
ThreadPoolExecutor.AbortPolicy
:抛出RejectedExecutionException
来拒绝新任务的处理。 -
ThreadPoolExecutor.CallerRunsPolicy
:调用执行自己的线程运行任务。该策略不会进行任务请求。但是这种策略会降低新任务的提交速度,影响程序的整体性能。另外,该策略喜欢增加队列容量。如果您的应用程序可以承受此延迟并且不能丢弃任何一个任务请求的话,你可以选择这个策略。 -
ThreadPoolExecutor.DiscardPolicy
:不处理新任务,直接丢弃掉。 -
ThreadPoolExecutor.DiscardOldestPolicy
:将丢弃最早的未处理的任务请求。
参考
JavaGuide面试突击版。
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 猿进化系列17——实战之一文学会前后端分离套路
- 基于Java的模拟写字板的设计与实现
- 猿思考系列2——一文搞懂同步并发套路
- 猿思考系列3——一文搞懂单例和思考的套路
- 猿思考系列3——一文学会思考的正确姿势
- 猿思考系列4——一文学会java的斗转星移动
- 猿思考系列5——一文明白java和微商那点儿事儿
- 猿思考系列8——缓存的套路也就这些
- 猿思考系列9——一文获取隐藏逻辑挖掘办法
- 猿蜕变系列1——春天的故事
- 猿蜕变系列2——一文搞懂spring的花式DI
- 猿蜕变系列3——SpringMVC之初体验
- 猿蜕变4——一文获取web框架正确学习套路
- 猿蜕变系列5——一文搞懂Controller的花式编写
- 猿蜕变系列6——一文掌握springMVC必会技巧