写在前面
假如我的项目中有一个非常不重要的链路,偶尔需要执行一下。在线程池设计的时间,我想到了线程池的八股文。于是为了尽大概节约资源,于是我把“常驻”的焦点线程数配置成了0,这样的线程池能执行使命吗?
线程池八股文回顾
使命投递时,有以下几种计谋:
- 线程池线程数量 < 焦点线程数,则创建一个新的线程执行使命
- 线程池线程数量 >= 焦点线程数
** 阻塞队列未满:投递使命进阻塞队列中
** 阻塞队列已满
*** 线程池线程数量 < 最大线程数:创建新的线程执行当前使命
*** 线程池线程数量 >= 最大线程数:执行拒绝计谋
大概得流程就是如上了,那corePoolSize == 0 的时间,按照上面的八股文,难道会直接投递进阻塞队列中等待执行吗?如果队列够大,会不会使命一直无法执行(因为队列没满,一直也不会创建“空闲线程”)
举个栗子
靠猜是没有用的,我们实践出真知。 下面我配置了一个线程池,期望投递使命的时间,能够通过“空闲线程”短时间帮忙处理,终极自行销毁掉,不持续占用系统资源。 那这种线程池在执行过程中有大概因为阻塞队列未满,终极使命迟迟没有执行吗?- private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
- 0,
- 8,
- 5, TimeUnit.MINUTES,
- new LinkedBlockingQueue<>(512),
- new CallerRunsPolicy()
- );
复制代码 这里使用这个线程池打印一个字符串,看看是否能够执行就好了- public static void main(String[] args) {
- MonitorThreadPoolConfig.addThreadPoolExecutor(threadPoolExecutor);
-
- threadPoolExecutor.execute(() -> System.out.println("hahahhaha "));
- }
复制代码 通过执行结果可以得知:使命是会被执行的(这里我加了对线程池的定时埋点监控)
探寻源码
- public void execute(Runnable command) {
- if (command == null)
- throw new NullPointerException();
- /*
- * Proceed in 3 steps:
- *
- * 1. If fewer than corePoolSize threads are running, try to
- * start a new thread with the given command as its first
- * task. The call to addWorker atomically checks runState and
- * workerCount, and so prevents false alarms that would add
- * threads when it shouldn't, by returning false.
- *
- * 2. If a task can be successfully queued, then we still need
- * to double-check whether we should have added a thread
- * (because existing ones died since last checking) or that
- * the pool shut down since entry into this method. So we
- * recheck state and if necessary roll back the enqueuing if
- * stopped, or start a new thread if there are none.
- *
- * 3. If we cannot queue task, then we try to add a new
- * thread. If it fails, we know we are shut down or saturated
- * and so reject the task.
- */
- int c = ctl.get();
- if (workerCountOf(c) < corePoolSize) {
- if (addWorker(command, true))
- return;
- c = ctl.get();
- if (isRunning(c) && workQueue.offer(command)) {
- int recheck = ctl.get();
- if (! isRunning(recheck) && remove(command))
- reject(command);
- else if (workerCountOf(recheck) == 0)
- addWorker(null, false);
- }
- else if (!addWorker(command, false))
- reject(command);
- }
复制代码 其实这个问题也不复杂,从粘贴出来的第34行 - 第35行可以看出:当焦点线程数 == 0的时间会创建一个新的线程执行当前的使命
拓展一下
既然可以通过corePoolSize == 0的方式来尽大概淘汰”常驻“线程的资源占用,那有没有别的办法可以达到同样的结果呢?
八股文战神大概会抢答了:ThreadPoolExecutor自1.6开始有一个属性如下,它的作用是支持焦点线程超时销毁- /**
- * If false (default), core threads stay alive even when idle.
- * If true, core threads use keepAliveTime to time out waiting
- * for work.
- */
- private volatile boolean allowCoreThreadTimeOut;
复制代码 于是我们可以将线程池配置改成如下再试一下(留意我把超时时间改成了5秒钟):- private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
- 1,
- 8,
- 5, TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(512),
- new CallerRunsPolicy()
- );
-
- public static void main(String[] args) {
- MonitorThreadPoolConfig.addThreadPoolExecutor(threadPoolExecutor);
- threadPoolExecutor.allowCoreThreadTimeOut(true); //核心线程数可以超时
-
- threadPoolExecutor.execute(() -> System.out.println("hahahhaha "));
- }
复制代码 执行结果如下,符合我们的预期。可以看到使命正常执行、线程池的线程数量初始是0,随后是1,末了天然销毁了,酿成了0。
总结
- 当焦点线程数设置为0的时间,在使命投递后,线程池内部会创建一个新的线程来执行使命(固然你的最大线程数配置要大于0)
- 针对功能点执行频率极低的场景,我们可以使用文中形貌的两种方式(1. 焦点线程数为零 2.
- naallowCoreThreadTimeOut == true)来让线程池临时创建可超时销毁的线程来执行使命
ps:据说JDK1.6之前corePoolSize == 0的话,线程池真的大概会出现投递进阻塞队列后没有线程执行的尴尬。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |