From 2c53e9e308dc39c5988c014a12d32917f0b5528d Mon Sep 17 00:00:00 2001 From: Federico Donnarumma Date: Fri, 25 Nov 2016 17:33:49 -0300 Subject: [PATCH 1/2] Expose prestartAllCoreThreads on ExecutorService See gh-1246 --- .../ThreadPoolExecutorFactoryBean.java | 21 +++++- .../ThreadPoolExecutorFactoryBeanTests.java | 64 +++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java index e880489f80..26dcec6370 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java @@ -73,12 +73,14 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport private int keepAliveSeconds = 60; - private boolean allowCoreThreadTimeOut = false; - private int queueCapacity = Integer.MAX_VALUE; + private boolean allowCoreThreadTimeOut = false; + private boolean exposeUnconfigurableExecutor = false; + private boolean prestartAllCoreThreads = false; + @Nullable private ExecutorService exposedExecutor; @@ -130,6 +132,17 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport this.queueCapacity = queueCapacity; } + /** + * Specify whether this FactoryBean should prestart all threads + * for the created executor. + *

Default is "false". + * Switch this flag to "true" to prestart the threads allocated for the current executor + * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads + */ + public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) { + this.prestartAllCoreThreads = prestartAllCoreThreads; + } + /** * Specify whether this FactoryBean should expose an unconfigurable * decorator for the created executor. @@ -154,6 +167,10 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport executor.allowCoreThreadTimeOut(true); } + if (this.prestartAllCoreThreads) { + executor.prestartAllCoreThreads(); + } + // Wrap executor with an unconfigurable decorator. this.exposedExecutor = (this.exposeUnconfigurableExecutor ? Executors.unconfigurableExecutorService(executor) : executor); diff --git a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java index abba6cf168..c5731e3139 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java @@ -16,17 +16,25 @@ package org.springframework.scheduling.concurrent; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; /** * @author Juergen Hoeller @@ -44,6 +52,21 @@ class ThreadPoolExecutorFactoryBeanTests { context.close(); } + @Test + public void executorWithPreStartedThreads() throws Exception { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfigWithPreStartedThreads.class); + ThreadPoolExecutor executor = context.getBean("childExecutor", ThreadPoolExecutor.class); + + verify(executor).prestartAllCoreThreads(); + } + + @Test + public void executorWithNoPreStartedThreads() throws Exception { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfigWithNoPreStartedThreads.class); + ThreadPoolExecutor executor = context.getBean("childExecutor", ThreadPoolExecutor.class); + + verify(executor, never()).prestartAllCoreThreads(); + } @Configuration static class ExecutorConfig { @@ -55,4 +78,45 @@ class ThreadPoolExecutorFactoryBeanTests { } + @Configuration + public static class ExecutorConfigWithPreStartedThreads { + + @Bean + public ThreadPoolExecutorFactoryBean executorChildFactory() { + ThreadPoolExecutorFactoryBeanMockingChild threadPoolExecutorFactoryBeanMockingChild = new ThreadPoolExecutorFactoryBeanMockingChild(); + threadPoolExecutorFactoryBeanMockingChild.setPrestartAllCoreThreads(true); + return threadPoolExecutorFactoryBeanMockingChild; + } + + @Bean + public ExecutorService childExecutor() { + return executorChildFactory().getObject(); + } + } + + @Configuration + public static class ExecutorConfigWithNoPreStartedThreads { + + @Bean + public ThreadPoolExecutorFactoryBean executorChildFactory() { + return new ThreadPoolExecutorFactoryBeanMockingChild(); + } + + @Bean + public ExecutorService childExecutor() { + return executorChildFactory().getObject(); + } + } + + private static class ThreadPoolExecutorFactoryBeanMockingChild extends ThreadPoolExecutorFactoryBean { + @Override + protected ThreadPoolExecutor createExecutor( + int corePoolSize, int maxPoolSize, int keepAliveSeconds, BlockingQueue queue, + ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { + + return mock(ThreadPoolExecutor.class); + } + } + + } From 19a8b94b21a3cd8740b06b9910734cd136b226ad Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 23 Nov 2021 17:15:18 +0100 Subject: [PATCH 2/2] Polish "Expose prestartAllCoreThreads on ExecutorService" See gh-1246 --- .../ThreadPoolExecutorFactoryBean.java | 32 +++++----- .../concurrent/ThreadPoolTaskExecutor.java | 17 ++++- .../ThreadPoolExecutorFactoryBeanTests.java | 64 ++++++------------- 3 files changed, 52 insertions(+), 61 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java index 26dcec6370..d95fef71f2 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,14 +73,14 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport private int keepAliveSeconds = 60; - private int queueCapacity = Integer.MAX_VALUE; - private boolean allowCoreThreadTimeOut = false; - private boolean exposeUnconfigurableExecutor = false; - private boolean prestartAllCoreThreads = false; + private int queueCapacity = Integer.MAX_VALUE; + + private boolean exposeUnconfigurableExecutor = false; + @Nullable private ExecutorService exposedExecutor; @@ -120,6 +120,16 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; } + /** + * Specify whether to start all core threads, causing them to idly wait for work. + *

Default is "false". + * @since 5.3.14 + * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads + */ + public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) { + this.prestartAllCoreThreads = prestartAllCoreThreads; + } + /** * Set the capacity for the ThreadPoolExecutor's BlockingQueue. * Default is {@code Integer.MAX_VALUE}. @@ -132,17 +142,6 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport this.queueCapacity = queueCapacity; } - /** - * Specify whether this FactoryBean should prestart all threads - * for the created executor. - *

Default is "false". - * Switch this flag to "true" to prestart the threads allocated for the current executor - * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads - */ - public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) { - this.prestartAllCoreThreads = prestartAllCoreThreads; - } - /** * Specify whether this FactoryBean should expose an unconfigurable * decorator for the created executor. @@ -166,7 +165,6 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport if (this.allowCoreThreadTimeOut) { executor.allowCoreThreadTimeOut(true); } - if (this.prestartAllCoreThreads) { executor.prestartAllCoreThreads(); } diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java index 486e0c7cdd..928814524c 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,6 +94,8 @@ public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport private boolean allowCoreThreadTimeOut = false; + private boolean prestartAllCoreThreads = false; + @Nullable private TaskDecorator taskDecorator; @@ -197,6 +199,16 @@ public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; } + /** + * Specify whether to start all core threads, causing them to idly wait for work. + *

Default is "false". + * @since 5.3.14 + * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads + */ + public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) { + this.prestartAllCoreThreads = prestartAllCoreThreads; + } + /** * Specify a custom {@link TaskDecorator} to be applied to any {@link Runnable} * about to be executed. @@ -256,6 +268,9 @@ public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport if (this.allowCoreThreadTimeOut) { executor.allowCoreThreadTimeOut(true); } + if (this.prestartAllCoreThreads) { + executor.prestartAllCoreThreads(); + } this.threadPoolExecutor = executor; return executor; diff --git a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java index c5731e3139..d350341a8c 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,11 +25,11 @@ import java.util.concurrent.ThreadPoolExecutor; import org.junit.jupiter.api.Test; -import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.GenericApplicationContext; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -37,6 +37,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; /** + * Tests for {@link ThreadPoolExecutorFactoryBean}. + * * @author Juergen Hoeller */ class ThreadPoolExecutorFactoryBeanTests { @@ -53,19 +55,25 @@ class ThreadPoolExecutorFactoryBeanTests { } @Test - public void executorWithPreStartedThreads() throws Exception { - ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfigWithPreStartedThreads.class); - ThreadPoolExecutor executor = context.getBean("childExecutor", ThreadPoolExecutor.class); - - verify(executor).prestartAllCoreThreads(); + void executorWithDefaultSettingsDoesNotPrestartAllCoreThreads() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBean("taskExecutor", ThreadPoolExecutorFactoryBean.class, TestThreadPoolExecutorFactoryBean::new); + context.refresh(); + ThreadPoolExecutor threadPoolExecutor = context.getBean(ThreadPoolExecutor.class); + verify(threadPoolExecutor, never()).prestartAllCoreThreads(); } @Test - public void executorWithNoPreStartedThreads() throws Exception { - ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfigWithNoPreStartedThreads.class); - ThreadPoolExecutor executor = context.getBean("childExecutor", ThreadPoolExecutor.class); - - verify(executor, never()).prestartAllCoreThreads(); + void executorWithPrestartAllCoreThreads() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBean("taskExecutor", ThreadPoolExecutorFactoryBean.class, () -> { + TestThreadPoolExecutorFactoryBean factoryBean = new TestThreadPoolExecutorFactoryBean(); + factoryBean.setPrestartAllCoreThreads(true); + return factoryBean; + }); + context.refresh(); + ThreadPoolExecutor threadPoolExecutor = context.getBean(ThreadPoolExecutor.class); + verify(threadPoolExecutor).prestartAllCoreThreads(); } @Configuration @@ -78,37 +86,8 @@ class ThreadPoolExecutorFactoryBeanTests { } - @Configuration - public static class ExecutorConfigWithPreStartedThreads { - - @Bean - public ThreadPoolExecutorFactoryBean executorChildFactory() { - ThreadPoolExecutorFactoryBeanMockingChild threadPoolExecutorFactoryBeanMockingChild = new ThreadPoolExecutorFactoryBeanMockingChild(); - threadPoolExecutorFactoryBeanMockingChild.setPrestartAllCoreThreads(true); - return threadPoolExecutorFactoryBeanMockingChild; - } - - @Bean - public ExecutorService childExecutor() { - return executorChildFactory().getObject(); - } - } + private static class TestThreadPoolExecutorFactoryBean extends ThreadPoolExecutorFactoryBean { - @Configuration - public static class ExecutorConfigWithNoPreStartedThreads { - - @Bean - public ThreadPoolExecutorFactoryBean executorChildFactory() { - return new ThreadPoolExecutorFactoryBeanMockingChild(); - } - - @Bean - public ExecutorService childExecutor() { - return executorChildFactory().getObject(); - } - } - - private static class ThreadPoolExecutorFactoryBeanMockingChild extends ThreadPoolExecutorFactoryBean { @Override protected ThreadPoolExecutor createExecutor( int corePoolSize, int maxPoolSize, int keepAliveSeconds, BlockingQueue queue, @@ -118,5 +97,4 @@ class ThreadPoolExecutorFactoryBeanTests { } } - }