hippo4j
hippo4j copied to clipboard
[Question] Provide boolean parameter to support prestartCoreThread or prestartAllCoreThreads
需求建议
请在提交问题之前回答这些问题,谢谢。
您的功能请求是否与问题有关?
是
描述你想要的功能
- config 模式
ExecutorProperties 提供 boolean 参数支持 prestartCoreThread or prestartAllCoreThreads
-
server 模式可提供相关按钮和接口来支持 prestartCoreThread or prestartAllCoreThreads
-
支持该功能的必要性
目前如果用户有此需求,会通过手动调用 prestartCoreThread or prestartAllCoreThreads。假设以下场景:config 模式下
@Bean
@DynamicThreadPool
public ThreadPoolExecutor threadPoolExecutor() {
ThreadPoolExecutor tpe = ThreadPoolBuilder.builder()
.corePoolSize(10)
.maxPoolNum(10)
.threadFactory("xxx")
.threadPoolId("xxx")
.waitForTasksToCompleteOnShutdown(true)
.dynamicPool()
.build();
tpe.prestartAllCoreThreads();
return tpe;
}
配置文件中指定:
thread-pool-id: 'xxx'
core-pool-size: 10
maximum-pool-size: 10
blocking-queue: 'LinkedBlockingQueue'
queue-capacity: 100
-
build() 时 tpe 中阻塞队列会设置默认 queue(称为 queue1),调用 prestartAllCoreThreads 后,10 条核心线程阻塞在 queue1 中等待获取任务
-
后续通过配置将 tpe 中阻塞队列设置为新的 queue(称为 queue2)
// DynamicThreadPoolPostProcessor#threadPoolParamReplace
private void threadPoolParamReplace(ThreadPoolExecutor executor, ExecutorProperties executorProperties) {
BlockingQueue workQueue = BlockingQueueTypeEnum.createBlockingQueue(executorProperties.getBlockingQueue(), executorProperties.getQueueCapacity());
ReflectUtil.setFieldValue(executor, "workQueue", workQueue);
...
}
- 当服务启动完成,tpe 开始工作时,该线程池中的 10 条线程将永远阻塞在 queue1 等待获取任务,但其实所有任务进 queue2 却无线程去执行 queue2 中的任务,导致后续出现问题
在替换动态线程池前,会有任务执行么?
@magestacks 更新了一下之前的描述,详细描述了下场景
@magestacks 复现步骤:
- 新建 springboot 项目,引入 hippo4j-config-spring-boot-starter 1.4.3-upgrade
- application.yml
spring:
dynamic:
thread-pool:
banner: false
notify-platforms:
executors:
- thread-pool-id: 'xxx'
core-pool-size: 5
maximum-pool-size: 5
blocking-queue: 'LinkedBlockingQueue'
queue-capacity: 10
keep-alive-time: 60
allow-core-thread-time-out: false
- DemoApplication.java
@SpringBootApplication
@EnableDynamicThreadPool
public class DemoApplication {
@Bean
@DynamicThreadPool
public ThreadPoolExecutor testThreadPoolExecutor() {
ThreadPoolExecutor tpe = ThreadPoolBuilder.builder()
.corePoolSize(5)
.maxPoolNum(5)
.threadFactory("xxx")
.threadPoolId("xxx")
.dynamicPool()
.build();
tpe.prestartAllCoreThreads();
return tpe;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- TestController.java
@Slf4j
@RestController
@RequestMapping("/")
public class TestController {
@Autowired
private ThreadPoolExecutor testThreadPoolExecutor;
@GetMapping("test")
public void test() {
for (int i = 0; i < 20; i++) {
testThreadPoolExecutor.execute(() -> System.out.println("ignore"));
BlockingQueue<Runnable> queue = testThreadPoolExecutor.getQueue();
log.info("queue.size(): " + queue.size());
log.info("queue.remainingCapacity(): " + queue.remainingCapacity());
}
}
}
- 启动服务请求 http://localhost:8080/test 日志:
- 原因:
圈错了,看 workqueue
该线程池中的 5 条线程将永远阻塞在 queue 6354 等待获取任务,但其实所有任务进 queue 6385 却无线程去执行 queue 6385 中的任务,导致后续队列满出现拒绝策略
是的。建议线程池的queue不要直接替换,而应该使用代理的方式,将原始的queue取出完成代理后使用反射(<jdk8)或unsafe(>=jdk8)替换原始的queue引用就可以了。 只要线程池内在被代理前有活动线程或者queue里已经有任务时就会出现上边的情况。但只要queue替换前有线程启动就会有问题,问题的大小就看启动的线程多少了。