使用ThreadPoolExecutor缩放maxPoolSize;为什么池不会动态增加其大小?
我正在学习有关线程,并在Java作为一个初学者并发包,我已经通过文件有关的ThreadPoolExecutor明白之间的差别消失了getPoolSize(),getCorePoolSize(),getMaxPoolSize()并试图实现代码相同。
一点背景
根据我的理解,Executors.newFixedThreadPool(3)创建一个 corePoolSize=3 的池,当我们继续通过池执行任务时,线程将被创建直到 3,然后当底层队列大小达到 100 并且仍然提交新任务时,这就是 maxPoolSize 进入图片和线程的地方现在从 corePoolSize 缩放到 maxPoolSize。
public class Test {
static ThreadPoolExecutor pool=(ThreadPoolExecutor)Executors.newFixedThreadPool(3);
public static void main(String k[]) throws InterruptedException{
BlockingQueue<Runnable> queue=pool.getQueue();
pool.execute(()->{
for(int b=0;b<10;b++)
System.out.println("Hello "+b);
});
pool.execute(()->{
for(int b=0;b<10;b++)
System.out.println("Hello "+b);
});
pool.setMaximumPoolSize(10); //Setting maxPoolSize
for(int j=0;j<20000;j++)
pool.execute(()->{
for(int b=0;b<100;b++){
System.out.println("Hello "+b);
System.out.println("Queue size "+queue.size()+" "+"and pool size "+pool.getPoolSize());
}
});
}
执行上述程序时,我可以看到队列大小达到 b/w 12000-20000,如果是这种情况,则getPoolSize()必须打印大于 corePoolSize 的值,因为 maxPoolSize 设置为 10 但每次它只会打印 3(即是 corePoolSize) 为什么会发生这种情况?正如我们所期望的那样,它可以扩展到 maxPoolSize。
回答
在Executors.newFixedThreadPool返回的ExecutorService; 一个不公开(并且可能是很好的理由)方法的接口,例如setMaximumPoolSize和setCorePoolSize。
如果创建类型为 的池Executors.newFixedThreadPool,则该池应在应用程序的生命周期内保持固定。如果您想要一个可以调整其大小的池,则应Executors.newCachedThreadPool()改为使用。
根据我的理解,Executors.newFixedThreadPool(3) 创建了一个带有 corePoolSize-3 (...)
查看一个实现newFixedThreadPool可以看到:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
和
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
因此,当您传递3给Executors.newFixedThreadPool构造函数时,您将the和 the都设置为。corePoolSizemaximumPoolSize3
(...) 并且当我们通过池继续执行任务时,线程将被创建直到 3,然后当底层队列大小达到 100 并且仍然提交新任务时,这就是 maxPoolSize 出现的地方,并且线程被缩放以达到 maxPoolSize 从现在的 corePoolSize。
实际上,这不是很准确,但我稍后会更详细地解释。现在阅读它的 Java 文档Executors.newFixedThreadPool指出:
创建一个线程池,该线程池重用固定数量的线程在共享的无界队列中运行。在任何时候,最多 nThreads 个线程将是活动的处理任务。如果在所有线程都处于活动状态时提交了其他任务,它们将在队列中等待,直到有线程可用。
所以没有池的扩展(除非你明确这样做)。
执行上述程序时,我可以看到队列大小达到 b/w 12000-20000,如果是这种情况,则 getPoolSize() 必须打印大于 corePoolSize 的值,因为 maxPoolSize 设置为 10 但每次它只会打印 3(这是 corePoolSize) 为什么会发生这种情况?因为我们期望它可以扩展到 maxPoolSize。
不,这是不准确的,如果你打印 pool.getMaximumPoolSize()它会10按预期返回,调用pool.setMaximumPoolSize(10);不会改变 corePoolSize 大小。但是,如果这样做,pool.setCorePoolSize(10);您将增加池以能够10同时处理线程。
在this.maximumPoolSize刚刚定义上限的多少个线程池应同时处理,它不会改变池的当前大小。
为什么池不会动态增加其大小?
更深入地newFixedThreadPool查看实现,可以看到 Pool 是使用new LinkedBlockingQueue<Runnable>()大小等于的任务队列初始化的Integer.MAX_VALUE。查看该方法execute可以在评论中阅读以下内容:
/*
* 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.
*/
如果仔细阅读第 2 点和第 3 点,可以推断只有当无法将任务添加到队列时,池才会创建比 corePoolSize 指定的线程更多的线程。并且由于Executors.newFixedThreadPool使用队列,Integer.MAX_VALUE您无法看到池动态分配更多资源,除非明确设置corePoolSizewith pool.setCorePoolSize。
所有这些都是人们不必关心的实现细节。因此,为什么Executors接口不公开诸如setMaximumPoolSize.
从ThreadPoolExecutor文档可以阅读:
核心和最大池大小
ThreadPoolExecutor 将根据 corePoolSize(请参阅 getCorePoolSize())和 maximumPoolSize(请参阅 getMaximumPoolSize())设置的边界自动调整池大小(请参阅 getPoolSize())。当在方法 execute(java.lang.Runnable) 中提交新任务,并且运行的线程少于 corePoolSize 时,即使其他工作线程空闲,也会创建一个新线程来处理请求。
如果有超过 corePoolSize 但小于 maximumPoolSize 的线程正在运行,则只有在队列已满时才会创建新线程。通过将 corePoolSize 和 maximumPoolSize 设置为相同,您可以创建一个固定大小的线程池。通过将maximumPoolSize 设置为一个基本上无界的值,例如Integer.MAX_VALUE,您可以允许池容纳任意数量的并发任务。最典型的是,核心和最大池大小仅在构造时设置,但它们也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 动态更改。
这基本上证实了为什么池没有动态更新其大小。