ThreadPoolExecutor点滴

线程池应该也是面试绕不开的一个点,平时大家也没少用,但其实也有一些小Tips还是值得记录一下。

Constructor

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
  • corePoolSize-线程池中保留的线程数量——尽管空闲(如果allowCoreThreadTimeOut被调用或者设置,在达到keepAliveTime后会销毁)
  • maximumPoolSize-线程池中允许的最大线程数量
  • keepAliveTime-线程数量超过核心线程数,超出的空闲线程最大存活时间
  • unit-存活时间单位
  • workQueue-任务队列,只接受通过execute方法提交的任务,最好自定义队列为有界队列,否则默认是Integer.MAX_VALUE(2147483647),内存会被挤压的任务压爆
  • threadFactory-线程创建工厂(一个接口),默认是Executors.defaultThreadFactory(),可以设置线程的名字(比如设置更有业务含义的名字,方便定位问题),是否是守护线程,设置优先级等
  • handler-拒绝策略handler,默认的有4个:1. 直接拒绝抛出异常;2. 交给提交线程来执行;3. 丢弃最旧提交但是没有执行的任务;4. 直接丢弃

一个任务提交到线程池的执行流程如下:

  1. 当前活动线程小于核心线程数,直接新起线程运行
  2. 当前活动线程大于核心线程数,入阻塞任务队列
  3. 当前活动线程大于核心线程数,阻塞任务队列已满,检查活动线程是否达到最大线程数,没有则新开一个线程,否则执行拒绝策略

线程池的状态

注意是线程池的状态而非池中处理任务线程的状态,代码中用一个int来存放线程池的状态(高三位)和活动线程数(低29位)。

  • RUNNING(-1<<29):可以接受新任务和处理队列中的任务
  • SHUTDOWN(0<<29):不接受新的任务,但会继续处理队列中的任务
  • STOP(1<<29): 不接受新的任务,也不继续处理队列中的任务,且会打断正在处理中的任务
  • TIDYING(2<<29):所有的任务都terminated,worker数是0,线程转移到该状态会运行terminated()钩子方法
  • TERMINATED(3<<29): terminated()执行完毕

状态之间的转移:

  • RUNNING -> SHUTDOWN, shutdown()被显示调用
  • (RUNNING OR SHUTDOWN) -> STOP, shutdownNow()被显示调用
  • SHUTDOWN -> TIDYING, 队列和池都空
  • STOP -> TIDYING, 池空
  • TIDYING -> TERMINATED, terminated()执行完毕
// 主线程池控制状态(control state)ctl,把workerCount和runState聚到到一个变量中,runStateOf/workerCountOf得到当前线程的状态和运行数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int CAPACITY = (1 << 29) - 1;
// c就是ctl
// 得到高3位
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 得到低29位
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

几个容易被忽略的API

在面试中被问到过一次这样的问题:想让线程池在初始化时就干活,而不是等到第一次提交任务时才创建线程,该怎么做?

这个问题在实际中应该是存在的,毕竟线程的创建是有开销的,既然创建了线程池肯定是为了用,也就不存在需要延迟创建之类的需求,不如在系统启动时就将线程池中的线程创建好,这样来了任务就可以直接交由线程处理。

翻看线程池的doc,可以看到有这样两个方法

/**
* Starts a core thread, causing it to idly wait for work. This overrides the default policy of starting core threads only when new tasks are executed. This method will return false if all core threads have already been started.
*/
public boolean prestartCoreThread() {
return workerCountOf(ctl.get()) < corePoolSize &&
addWorker(null, true);
} /**
Starts all core threads, causing them to idly wait for work. This overrides the default policy of starting core threads only when new tasks are executed.
*/
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}

从源代码可以发现两个方法默认的策略是只有第一个任务到达时才会创建线程,可以覆盖这两个方法,然后在创建线程池时调用,就可以达到目的。

@Override
public boolean prestartCoreThread() {
execute(() -> System.out.println("start a core thread"));
return true;
} @Override
public int prestartAllCoreThreads() {
int n = 0;
while (++n < coreSize) {
execute(() -> System.out.println("start a core thread"));
}
return n;
}

此外还有两个hook方法:beforeExecute和afterExecute,也可以通过覆盖这两个方法,在一个task执行的前后做一些事情。

/**
t - the thread that will run task r
r - the task that will be executed
*/
@Override
protected void beforeExecute(Thread t, Runnable r) {
System.out.println("---before log---");
super.beforeExecute(t, r);
} /**
r - the runnable that has completed
t - the exception that caused termination, or null if execution completed normally
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
System.out.println("---after log---");
}

END

当然线程池还有其他很多知识点,鉴于大多数的文章都会涉及到我也不在赘述了,这两个扩展点平时中用到的比较少,所以权当笔记和扩展一点自己的知识点,无论是面试还是实践,感觉按照场景来都有些许用处。

记一次ThreadPoolExecutor面试的更多相关文章

  1. 记一次HashMap面试

    记一次HashMap面试 从网上已经身边同事朋友的面试情况来看,面试HashMap几乎是必问的,网上也很多类似的文章,但是真面起来,发现还是有很多点可以深抠的.本篇就结合一次面试经历说一下之前没有注意 ...

  2. 对于大学4年的反思(续),记我的ThoughtWorks面试

    之前我写了一篇对于大学四年的反思,时隔一个月,为什么我这么快就要来写这篇续章呢?主要有两个原因,第一是感谢静子姐姐,记得知乎上有个回答里面说过人生需要有贵人的帮助,遇到贵人是一件很幸运的事情.我想,静 ...

  3. 记一次Java面试问题点总结

    引言 昨日接了一个阿里外包的电话面试,问了一些技术问题感觉到自己是真的菜,接触Java开发已经也有一段时间,技术方面说来惭愧,一直以来只是局限于框架工具的用法,也没有进行了解其实现的原理,更重要的是一 ...

  4. Java Web架构知识整理——记一次阿里面试经历

    惭愧,从一次电面说起.我个人在某国企做一名软件设计师,国企大家都懂的,待遇一般而且没啥意思,做的方向基本都是操作系统.驱动和工具软件的开发,语言基本都是C/C++.最近也想跳槽,刚好有幸得到了一次阿里 ...

  5. 记一次 Google 面试经历

    本文由码农网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 这是我上周去面试的地方.很顺利,我觉得——至少我认为我已经尽我所能,并且无论发生什么事情对我都是有帮助的. 由于 ...

  6. 记一次C#面试

    最近参加了工作后的第一次面试,虽然最终没谈成,但是收获还是不少,不管是技术还是面试经验还是得多多积累呀. 这一次面试与在学校时候参加过的面试区别还是挺大的.校园招聘的面试问的问题似乎都比较具体,直接针 ...

  7. 记一次前端面试~终于拿到理想中的offer!

    2019年已经过去一半,终于拿到一直想去的公司offer,也算是实现了今年的一个小目标. 由于这家公司是我从去年到现在最想去的公司,本次换工作一直没有投,希望先积累下面试经验再投. 没有想到居然先在b ...

  8. 记一次被面试的final问题

    ---- 前言 今天面试被问到了,我们都知道final修饰的东西是不可变的,那么是值不可变还是其地址不可变?一脸懵逼,回来查阅一番,总结一下 --- final与数据 在日常行为下,一般数据指的都是基 ...

  9. <面试题分享> 记两次58面试

    说明 来北京找工作,有个猎头看我的简历不错,帮我投了两个58同城的面试,投的都比较高,题也注重原理,较难,这里分享出来,给有需要的人和自己提个醒,保持空杯 面试题内容 2019.05.07 北京58企 ...

随机推荐

  1. img 分区响应图

    ---恢复内容开始--- a标签的target为_blank属性,意为跳转到新的页面. shape要和coords配合使用,shape为rect时意义为矩形.shape 为不同属性时意为不同的形态触碰 ...

  2. Jmeter put 方法总结

    1.百度到很多关于jmeter put 方法的使用 ,但觉得都完全 下面我大致总结下 : >1.放入 url 中 如:www.*****.com?a=1&b=2 ; >2.放入到p ...

  3. git如何删除已经 add 的文件 (如何撤销已放入缓存区文件的修改)

    使用 git rm 命令即可,有两种选择, 一种是 git rm –cached “文件路径”,不删除物理文件,仅将该文件从缓存中删除: 一种是 git rm –f “文件路径”,不仅将该文件从缓存中 ...

  4. delphi 登陆窗口的问题 [问题点数:30分,结帖人tianhuo_soft]

    http://bbs.csdn.net/topics/200053917 有两个窗口formLogin是登陆窗口,formMain是主窗口我想让formMain显示出来,并且formLogin窗口关闭 ...

  5. P3254 圆桌问题

    题目链接 非常简单的一道网络流题 我们发现每个单位的人要坐到不同餐桌上,那也就是说每张餐桌上不能有同一单位的人.这样的话,我们对于每个单位向每张餐桌连一条边权为1的边,表示同一餐桌不得有相同单位的人. ...

  6. 【BZOJ1965】[AHOI2005]洗牌(数论)

    [BZOJ1965][AHOI2005]洗牌(数论) 题面 BZOJ 洛谷 题解 考虑反过来做这个洗牌的操作,假定当前牌是第\(l\)张. 因为之前洗的时候考虑了前一半和后一半,所以根据\(l\)的奇 ...

  7. 【BZOJ3232】圈地游戏(分数规划,网络流)

    [BZOJ3232]圈地游戏(分数规划,网络流) 题面 BZOJ 题解 很神仙的一道题. 首先看到最大化的比值很容易想到分数规划.现在考虑分数规划之后怎么计算贡献. 首先每条边的贡献就变成了\(mid ...

  8. 【Luogu P4074】[WC2013]糖果公园(树上带修改莫队)

    题目描述 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩. 糖果公园的结构十分奇特,它由 \(n\) 个游 ...

  9. 前端学习 -- Css -- 字体

    设置字体颜色,使用color来设置文字的颜色 设置文字的大小,浏览器中一般默认的文字大小都是16pxfont-size设置的并不是文字本身的大小,在页面中,每个文字都是处在一个看不见的框中的我们设置的 ...

  10. 解题:NOI 2012 骑行川藏

    题面 入手点是每段路程中能量$e$与时间$t$的关系,$t-e$这个函数的导数对于各个路段一样,否则我们可以从导数大的一段路抽出一部分能量分给导数小的,这样会更优 毕姥爷在考场上的做法:猜一猜,然后拿 ...