Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] Provide boolean parameter to support prestartCoreThread or prestartAllCoreThreads #1100

Open
jjiey opened this issue Mar 12, 2023 · 4 comments

Comments

@jjiey
Copy link
Contributor

jjiey commented Mar 12, 2023

需求建议

请在提交问题之前回答这些问题,谢谢。

您的功能请求是否与问题有关?

描述你想要的功能

  1. config 模式

ExecutorProperties 提供 boolean 参数支持 prestartCoreThread or prestartAllCoreThreads

  1. server 模式可提供相关按钮和接口来支持 prestartCoreThread or prestartAllCoreThreads

  2. 支持该功能的必要性

目前如果用户有此需求,会通过手动调用 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
Copy link
Member

在替换动态线程池前,会有任务执行么?

@jjiey
Copy link
Contributor Author

jjiey commented Mar 13, 2023

@magestacks 更新了一下之前的描述,详细描述了下场景

@jjiey
Copy link
Contributor Author

jjiey commented Mar 20, 2023

@magestacks 复现步骤:

  1. 新建 springboot 项目,引入 hippo4j-config-spring-boot-starter 1.4.3-upgrade
  2. 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
  1. 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);
    }
}
  1. 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());
        }
    }
}
  1. 启动服务请求 http://localhost:8080/test 日志:
    image
  2. 原因:
    image
    image
    圈错了,看 workqueue
    image
    image

该线程池中的 5 条线程将永远阻塞在 queue 6354 等待获取任务,但其实所有任务进 queue 6385 却无线程去执行 queue 6385 中的任务,导致后续队列满出现拒绝策略

@yanrongzhen yanrongzhen changed the title Provide boolean parameter to support prestartCoreThread or prestartAllCoreThreads [Question] Provide boolean parameter to support prestartCoreThread or prestartAllCoreThreads Sep 1, 2023
@fuxiuzhan
Copy link

fuxiuzhan commented Sep 7, 2023

是的。建议线程池的queue不要直接替换,而应该使用代理的方式,将原始的queue取出完成代理后使用反射(<jdk8)或unsafe(>=jdk8)替换原始的queue引用就可以了。
只要线程池内在被代理前有活动线程或者queue里已经有任务时就会出现上边的情况。但只要queue替换前有线程启动就会有问题,问题的大小就看启动的线程多少了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants