欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于线程池你不得不知道的一些设置

程序员文章站 2023-12-13 10:45:40
看完我上一篇文章,之后,当遇到这种问题,你觉得你完全能够唬住面试官了,50k轻松到手。殊不知,要是面试官此刻给你来个反杀: 初始化线程池时可以预先创建线程吗?线程池的核心...

看完我上一篇文章,之后,当遇到这种问题,你觉得你完全能够唬住面试官了,50k轻松到手。殊不知,要是面试官此刻给你来个反杀:

初始化线程池时可以预先创建线程吗?线程池的核心线程可以被回收吗?为什么?

如果此刻你一脸懵逼,这个要慌,问题很大,50k马上变5k。

关于线程池你不得不知道的一些设置

有细心的网友早就想到了这个问题:

关于线程池你不得不知道的一些设置

关于线程池你不得不知道的一些设置

在threadpoolexecutor线程池中,还有一些不常用的设置。我建议如果您在应用场景中没有特殊的要求,就不需要使用这些设置。

初始化线程池时可以预先创建线程吗?

prestartallcorethreads

初始化线程池时是可以预先创建线程的,初始化线程池后,再调用prestartallcorethreads()方法,即可预先创建corepoolsize数量的核心线程,我们看源码:

public int prestartallcorethreads() {
 int n = 0;
 while (addworker(null, true))
  ++n;
 return n;
}
private boolean addworker(runnable firsttask, boolean core) {
 // ..
}

addworker方法目的是在线程池中添加任务并执行,如果task为空,线程获取任务执行时调用gettask()方法,该方法从blockingqueue阻塞队列中阻塞获取任务执行,因此线程不会释放,留存在线程池中,如果core=true,说明任务只能利用核心线程来执行。

所以该方法会在线程池总预先创建没有任务执行的线程,数量为corepoolsize。

下面我们测试一下:

关于线程池你不得不知道的一些设置

从测试结果来看,线程池中已经预先创建了corepoolsize数量的空闲线程。

prestartcorethread

prestartcorethread()同样可以预先创建线程,只不过该方法只会与创建1条线程,我们来看源码:

public boolean prestartcorethread() {
 return workercountof(ctl.get()) < corepoolsize &&
  addworker(null, true);
}

 从方法源码可知,如果此时工作线程数量小于corepoolsize,那么就调用addworker创建1条空闲核心线程。

下面我们测试一下:

关于线程池你不得不知道的一些设置

从测试结果来看,线程池中已经预先创建了1条空闲线程。

线程池的核心线程可以被回收吗?

你可能会想到将corepoolsize的数量设置为0,从而线程池的所有线程都是“临时”的,只有keepalivetime存活时间,你的思路也许时正确的,但你有没有想过一个很严重的后果,corepoolsize=0时,任务需要填满阻塞队列才会创建线程来执行任务,阻塞队列有设置长度还好,如果队列长度无限大呢,你就等着oom异常吧,所以用这种设置行为并不是我们所需要的。

有没有什么设置可以回收核心线程呢?

allowcorethreadtimeout

threadpoolexecutor有一个私有成员变量:

private volatile boolean allowcorethreadtimeout;

 如果allowcorethreadtimeout=true,核心线程在规定时间内会被回收。

上面我也说了,当线程空闲时会从blockingqueue阻塞队列中阻塞获取任务执行,所以我们来看看是保证核心线程不被销毁的,我们直接定位到源码部位:

java.util.concurrent.threadpoolexecutor#gettask:

boolean timedout = false; // did the last poll() time out?
for (;;) {
 // are workers subject to culling?
 boolean timed = allowcorethreadtimeout || wc > corepoolsize;

 try {
  runnable r = timed ?
   workqueue.poll(keepalivetime, timeunit.nanoseconds) :
  workqueue.take();
  if (r != null)
   return r;
  timedout = true;
 } catch (interruptedexception retry) {
  timedout = false;
 }
}

这里的关键值timed,如果allowcorethreadtimeout=true或者此时工作线程大于corepoolsize,timed=true,如果timed=true,会调用poll()方法从阻塞队列中获取任务,否则调用take()方法获取任务。

下面我来解释这两个方法:

  1. poll(long timeout, timeunit unit):从blockingqueue取出一个任务,如果不能立即取出,则可以等待timeout参数的时间,如果超过这个时间还不能取出任务,则返回null;
  2. take():从blocking阻塞队列取出一个任务,如果blockingqueue为空,阻断进入等待状态直到blockingqueue有新的任务被加入为止。

到这里,我们就很好地解释了,当allowcorethreadtimeout=true或者此时工作线程大于corepoolsize时,线程调用blockingqueue的poll方法获取任务,若超过keepalivetime时间,则返回null,timedout=true,则gettask会返回null,线程中的runworker方法会退出while循环,线程接下来会被回收。

下面我们测试一下:

关于线程池你不得不知道的一些设置

以上所述是小编给大家介绍的java线程池设置详解整合,希望对大家有所帮助

上一篇:

下一篇: