【Java 并发编程】阿里最喜欢问的几道线程池的面试题?
引言
上一篇文章我们有介绍过线程池的一个基本执行流程《【Java并发编程】面试必备之线程池》以及它的7个核心参数,以及每个参数的作用、以及如何去使用线程池
还留了几个小问题。。建议看这篇文章之前可先看下前面那篇文章。这篇文章我们就来分析下上篇文章的几个小问题
线程池是否区分核心线程和非核心线程?
如何保证核心线程不被销毁?
线程池的线程是如何做到复用的?
我们先看最后一个问题一般一个线程执行完任务之后就结束了,Thread.start()
只能调用一次,一旦这个调用结束,则该线程就到了stop
状态,不能再次调用start
。如果你对一个已经启动的线程对象再调用一次start
方法的话,会产生:IllegalThreadStateException
异常,但是Thread
的run
方法是可以重复调用的。所以这里也会有一个面试经常问到的问题:Thread类中run()和start()方法的有什么区别?
下面我们就从jdk
的源码来一起看看如何实现线程复用的:
线程池执行任务的ThreadPoolExecutor
#execute
方法为入口
excute方法主要业务逻辑
如果当前的线程池运行线程小于coreSize,则创建新线程来执行任务。
如果当前运行的线程等于coreSize或多余coreSize(动态修改了coreSize才会出现这种情况),把任务放到阻塞队列中。
如果队列已满无法将新加入的任务放进去的话,则需要创建新的线程来执行任务。
如果新创建线程已经达到了最大线程数,任务将会被拒绝。
addWorker 方法
上述方法的核心主要就是addWorker方法,
这个方法我们先看看这个work类吧
work类实现了Runnable接口,然后run方法里面调用了runWorker方法
这个runwork
方法中会优先取worker
绑定的任务,如果创建这个worker的时候没有给worker
绑定任务,worker
就会从队列里面获取任务来执行,执行完之后worker
并不会销毁,而是通过while
循环不停的执行getTask
方法从阻塞队列中获取任务调用task.run()来执行任务,这样的话就达到了线程复用的目的。 while (task != null || (task = getTask()) != null)
这个循环条件只要getTask
返回获取的值不为空这个循环就不会终止, 这样线程也就会一直在运行。
那么任务执行完怎么保证核心线程不销毁?非核心线程销毁?
答案就在这个getTask()
方法里面
所以保证线程不被销毁的关键代码就是这一句代码
只要timed
为false
这个workQueue.take()
就会一直阻塞,也就保证了线程不会被销毁。timed
的值又是通过allowCoreThreadTimeOut
和正在运行的线程数量是否大于coreSize
控制的。
只要
getTask
方法返回null
我们的线程就会被回收(runWorker
方法会调用processWorkerExit
)这个方法的源码也就解释了为什么我们在创建线程池的时候设置了
allowCoreThreadTimeOut
=true
的话,核心线程也会进行销毁。通过这个方法我也们可以回答上面那个问题线程池是不区分核心线程和非核心线程的。
结束
由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
感谢您的阅读,十分欢迎并感谢您的关注。
巨人的肩膀摘苹果
http://objcoding.com/2019/04/25/threadpool-running/
版权声明: 本文为 InfoQ 作者【java金融】的原创文章。
原文链接:【http://xie.infoq.cn/article/f1470c77e28e6306069d8c5f7】。文章转载请联系作者。
评论