线程池的使用说明

线程池主要由两个概念组成:一个是任务队列;另一个是工作者线程。工作者线程主体就是一个循环,循环从队列中接受任务并执行,任务队列保存待执行的任务。

ThreadPoolExecutor

继承自AbstractExecutorService,实现了ExecutorService。

ThreadPoolExecutor实现了生产者/消费者模式,工作者线程就是消费者,任务提交者就是生产者,线程池自己维护任务队列。当我们碰到类似生产者/消费者问题时,应该优先考虑直接使用线程池,而非“重新发明轮子”,应自己管理和维护消费者线程及任务队列。

两个构造函数

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
						  long keepAliveTime, TimeUnit unit, 
						  BlockingQueue<Runnable> workQueue)
	
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
						  long keepAliveTime, TimeUnit unit, 
						  BlockingQueue<Runnable> workQueue,
						  ThreadFactory threadFactory, RejectedExecutionHandler handler)

第二个构造方法多了两个参数threadFactory和handler,这两个参数一般不需要,第一个构造方法会设置默认值。

参数说明

入参

说明

corePoolSize

核心线程个数

表示线程池中的核心线程个数,不是一开始就创建这么多线程,刚创建一个线程池后,实际上并不会创建任何线程。

有新任务到来的时候,如果当前线程个数小于corePoolSiz,就会创建一个新线程来执行该任务,需要说明的是,即使其他线程现在也是空闲的,也会创建新线程。

线程个数小于等于corePoolSize时,默认情况下:

  • 核心线程不会预先创建,只有当有任务时才会创建。
  • 核心线程不会因为空闲而被终止,keepAliveTime参数不适用于它。

不过,ThreadPoolExecutor有如下方法,可以改变这个默认行为:

//预先创建所有的核心线程

public int prestartAllCoreThreads()

//创建一个核心线程,如果所有核心线程都已创建,则返回false

public boolean prestartCoreThread()

//如果参数为true,则keepAliveTime参数也适用于核心线程

public void allowCoreThreadTimeOut(boolean value)

maximumPoolSize

最大线程个数

表示线程池中的最多线程数,线程的个数会动态变化,但这是最大值,不管有多少任务,都不会创建比这个值大的线程个数。

如果线程个数大于等于corePoolSize,那就不会立即创建新线程了,它会先尝试排队,需要强调的是,它是“尝试”排队,而不是“阻塞等待”入队,如果队列满了或其他原因不能立即入队,它就不会排队,而是检查线程个数是否达到了maximumPoolSize,如果没有,就会继续创建线程,直到线程数达到maximumPoolSize。

keepAliveTime

unit

空闲线程存活时间

keepAliveTime的目的是为了释放多余的线程资源,它表示,当线程池中的线程个数大于corePoolSize时额外空闲线程的存活时间。也就是说,一个非核心线程,在空闲等待新任务时,会有一个最长等待时间,即keepAliveTime,如果到了时间还是没有新任务,就会被终止。如果该值为0,则表示所有线程都不会超时终止。

workQueue

任务队列

要求的队列类型是阻塞队列BlockingQueue。

注意:

  • 如果用的是无界队列LinkedBlockingQueue,线程个数最多只能达到corePoolSize,到达corePoolSize后,新的任务总会排队,参数maximumPoolSize也就没有意义了。
  • 对于没有实际存储空间的同步阻塞队列SynchronousQueue,当尝试排队时,只有正好有空闲线程在等待接受任务时,才会入队成功,否则,总是会创建新线程,直到达到maximumPoolSize。

threadFactory

线程工厂

类型为接口ThreadFactory:

public interface ThreadFactory {

    Thread newThread(Runnable r);

}

这个接口根据Runnable创建一个Thread, ThreadPoolExecutor的默认实现是Executors类中的静态内部类DefaultThreadFactory,主要就是创建一个线程,给线程设置一个名称,设置daemon属性为false,设置线程优先级为标准默认优先级,线程名称的格式为:pool-<线程池编号>-thread-<线程编号>。

如果需要自定义一些线程的属性,比如名称,可以实现自定义的ThreadFactory。

handler

任务拒绝策略

如果队列有界,且maximumPoolSize有限,则当队列排满,线程个数也达到了maximumPoolSize,这时,新任务来了,会触发线程池的任务拒绝策略。

ThreadPoolExecutor实现了4种处理方式:

  1. ThreadPoolExecutor.AbortPolicy:这就是默认的方式,抛出异常。
  2. ThreadPoolExecutor.DiscardPolicy:静默处理,忽略新任务,不抛出异常,也不执行。
  3. ThreadPoolExecutor.DiscardOldestPolicy:将等待时间最长的任务扔掉,然后自己排队。
  4. ThreadPoolExecutor.CallerRunsPolicy:在任务提交者线程中执行任务,而不是交给线程池中的线程执行。

这些拒绝策略都实现RejectedExecutionHandler接口:

public interface RejectedExecutionHandler {

    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

}

当线程池不能接受任务时,调用其拒绝策略的rejectedExecution方法。

注意:

  • 拒绝策略只有在队列有界,且maximumPoolSize有限的情况下才会触发。
  • 如果队列无界,服务不了的任务总是会排队,但这不一定是期望的结果,因为请求处理队列可能会消耗非常大的内存,甚至引发内存不够的异常。
  • 如果队列有界但maximumPoolSize无限,可能会创建过多的线程,占满CPU和内存,使得任何任务都难以完成。

所以,在任务量非常大的场景中,让拒绝策略有机会执行是保证系统稳定运行很重要的方面。

查看关于线程和任务数的一些动态数字:

获取状态函数

说明

public int getPoolSize()

返回当前线程个数

public int getLargestPoolSize()

返回线程池曾经达到过的最大线程个数

public long getCompletedTaskCount()

返回线程池自创建以来所有已完成的任务数

public long getTaskCount()

返回所有任务数,包括所有已完成的加上所有排队待执行的

工厂类Executors

类Executors提供了一些静态工厂方法,可以方便地创建一些预配置的线程池,主要方法有:

public static ExecutorService newSingleThreadExecutor()
	
public static ExecutorService newFixedThreadPool(int nThreads)
	
public static ExecutorService newCachedThreadPool()

方法

说明

使用场景

newSingleThreadExecutor

只使用一个线程,使用无界队列LinkedBlockingQueue,线程创建后不会超时终止,该线程顺序执行所有任务。

需要确保所有任务被顺序执行的场合。

newFixedThreadPool

使用固定数目的n个线程,使用无界队列LinkedBlockingQueue,线程创建后不会超时终止。和newSingleThreadExecutor一样,由于是无界队列,如果排队任务过多,可能会消耗过多的内存。

在系统负载很高的情况下,newFixedThreadPool可以通过队列对新任务排队,保证有足够的资源处理实际的任务。

newCachedThreadPool

它的corePoolSize为0, maximumPoolSize为Integer.MAⅩ_VALUE, keepAliveTime是60秒,队列为SynchronousQueue。

它的含义是:当新任务到来时,如果正好有空闲线程在等待任务,则其中一个空闲线程接受该任务,否则就总是创建一个新线程,创建的总线程个数不受限制,对任一空闲线程,如果60秒内没有新任务,就终止。

如果系统负载不太高,单个任务的执行时间也比较短,newCachedThreadPool的效率可能更高,因为任务可以不经排队,直接交给某一个空闲线程。

注意:在系统负载可能极高的情况下,上述方式都不是好的选择,newFixedThreadPool的问题是队列过长,而newCachedThreadPool的问题是线程过多,这时,应根据具体情况自定义ThreadPoolExecutor,传递合适的参数。

#java原理##并发编程#
27届毕业生-Java知识专辑 文章被收录于专栏

知其然知其所以然,只有掌握了底层原理,借助第一性原理,才可以在日常开发和项目中运用自如,潇洒走江湖。 专为27届毕业生准备,托起您的就业梦。 该专辑会不定时更新,建议27届同学订阅,入职后扎实的基本功可以帮您争取更好的机会和项目。

全部评论
看完这篇文档,来检验下你的掌握情况,看看你是否可以找出其中的问题:https://www.nowcoder.com/discuss/859437338936487936
点赞 回复 分享
发布于 今天 14:26 北京

相关推荐

本人双九(一般的985)现在🐻厂实习字节hr两次电联同一个职位的实习,问我考不考虑,第二次说的很恳切🐻厂给的测开,字节给的后端本人还没确定,秋招走大厂还是走央国企,目前暂时偏向稳定,更倾向于在江浙一带工作犹豫的点:1.目前组内氛围好,mt也好,组长也好2.如果再面试,我需要重跑我的项目,再准备+八股+手撕,会很累3.如果准备不充分,怕脏面评02-04&nbsp;22:16&nbsp;已编辑&nbsp;北京收到1人送花1朵33大家都在搜:央国企全部评论&nbsp;(41条)推荐最新学也学不动玩也玩不爽02-08&nbsp;21:53门头沟学院&nbsp;C++过来人告诉你&nbsp;字节hr&nbsp;就是这德行面试前对每个候选人特别舔&nbsp;面完找他问进度就查无此人了山东227我不爱上班啊&nbsp;:哈哈哈哈哈,这几天在xhs上略有了解海捞吧hhh小何和&nbsp;:前期很热情,突然很冷淡😁超级无敌霹雳小猪猪02-05&nbsp;09:53深圳职业技术学院&nbsp;护士双九这么不自信的吗?广东27我不爱上班啊&nbsp;:我这个实习的准备时间短,感觉在接其他面试被拷打了,项目和技术栈不熟,就没什么自信ZYLOM05&nbsp;:稳定也挺好呀走呀走02-06&nbsp;12:10门头沟学院&nbsp;C++在面字节前面几家其它的后端面试会好一些江苏12我不爱上班啊&nbsp;:哈哈哈哈我同门面字节,一面二面就没有低于1h的,哈人小凡h02-15&nbsp;17:34四川大学&nbsp;天线工程师🐻厂留用率?甘肃01kaori__02-14&nbsp;13:32香港大学&nbsp;Java哥们儿你不知道字节是一线大厂里面发面最多的吗,发面试又不是oc,hr再恳切有作用吗北京11楼主&nbsp;匿名牛油&nbsp;:现在知道了哈哈哈越今朝002-11&nbsp;13:10四川大学&nbsp;后端工程师为啥要犹豫,字节不还是正常面试吗,又不是已经拿到oc了北京11楼主&nbsp;匿名牛油&nbsp;:不想脏面评,不过现在也无所谓了,也不准备去大厂在改简历的大卫很认真今天&nbsp;19:17浙江工业大学&nbsp;深度学习现在这么卷,0实习能进的,都是有真本事的浙江00我就不理解了02-27&nbsp;12:03京东&nbsp;后端开发工程师测开转后端也不错吧,不过HR虽然舔,面试要求不会放松的北京00ScarletMoon_02-11&nbsp;20:42南京理工大学&nbsp;Java🐻厂留用率?江苏10楼主&nbsp;匿名牛油&nbsp;:听说转正会比较容易?不是特别了解_hengheng02-10&nbsp;14:28阿里巴巴&nbsp;ai&nbsp;infra还以为是到hr面了让你去呢,就约个面试能有多诚恳北京00嵌入式的小白02-08&nbsp;21:50西安理工大学&nbsp;嵌入式软件开发那就赶紧准备啊,看看别人面经,刷八股啥的陕西10我不爱上班啊&nbsp;:暂时没有特别想换的准备,大概率all&nbsp;in央国企了哈基米奶龙02-08&nbsp;15:35北京邮电大学&nbsp;后端工程师字节hr都很热情吧,最近27届暑期开了,也收到几个电话&nbsp;但是考虑不想脏面评就不面了北京10我不爱上班啊&nbsp;:已经略有了解了,感觉很海捞不进大厂不改名😡x02-06&nbsp;15:34百度&nbsp;后端开发(实习)建议不要脏面评,年后投随便约面北京30坚持无悔意无休&nbsp;:666又遇到兄弟了我不爱上班啊&nbsp;:hr是说约年后不进大厂不改名😡x&nbsp;回复&nbsp;坚持无悔意无休&nbsp;:Jasonnnnnnnn02-06&nbsp;07:35Columbia&nbsp;University&nbsp;Java试一下咯美国10我不爱上班啊&nbsp;:暂时不考虑了hhh站队站对牛02-05&nbsp;22:49门头沟学院&nbsp;机械设计/制造双***历都到顶了江苏10我不爱上班啊&nbsp;:不是很厉害的9,曾经被嘲过牛客548622592号02-05&nbsp;20:55Java要不等年后吧,不差这一次吧??字节那不是投了就能面吗?一定要面这个岗?北京10我不爱上班啊&nbsp;:打电话也是准备说年后了,年前肯定不行哈哈哈我投的少,我也不知道是不是投了就能面主要怕我面得不好,唉StephenZ_02-05&nbsp;20:31广东工业大学&nbsp;Java双9干什么测开广东10我不爱上班啊&nbsp;:哈哈哈哈哈,准备实习的时间很短很仓促,收到的后端面试不多,想着先有个实习就走了测开我们都在等雨停02-05&nbsp;19:54门头沟学院&nbsp;后端工程师熊是什么厂广西10我不爱上班啊&nbsp;:百度秋招失业人02-05&nbsp;10:48National&nbsp;University&nbsp;of&nbsp;Singapore&nbsp;测试开发大胆去面吧&nbsp;我就是不自信加上没后端实习&nbsp;秋招大厂只投了测开&nbsp;现在有点后悔福建30我不爱上班啊&nbsp;:,不自信加上项目不熟,我感觉会被拷打所以害怕秋招失业人&nbsp;回复&nbsp;我不爱上班啊&nbsp;:没事&nbsp;不会损失啥&nbsp;等你真的拿了测开就会像我一样从激动欣喜到焦虑前景我不爱上班啊&nbsp;回复&nbsp;秋招失业人&nbsp;:还有一个就是怕字节脏面评,所以如果接面试肯定也会努力准备一下的如果秋招不满意的话,还能搏一搏春招吧,我身边就有在春招找到不错的岗位的我不爱上班啊02-04&nbsp;22:20未填写教育信息&nbsp;测试开发还有就是,emmmm,周围也有面字节的,普遍表示有难度,我觉得我菜
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务