线程池

线程池系列(6)如何统一管理旧项目中的线程池

2022-03-11  本文已影响0人  小胖学编程

问题:在一个项目中,每一台机器活跃线程数有时候会有1000-2000个。当机器中存在大量无用的活跃线程时,便会影响性能,那么如何处理这种情况???

1. 问题

本文不讲述线程池是如何被统一管理的。重点描述:在旧已有项目中如何找到并管理已经存在的线程池。

项目中线程池的用法五花八门:

  1. 在类属性中声明JDK线程池;
  2. 使用@Autowired自动注入Spring的线程池;
  3. 在一个公共工具类中声明多个JDK线程池,类的方法中直接使用线程池;

每个人编码风格不同,线程池的声明方式不同;每个人的对线程池的理解不同,所以核心参数的配置也就不同。导致了线程池的不可管理。

2. 解决方案

原理:借助于Spring的Bean的初始化流程+反射机制去解决这个问题。

项目在启动过程中,会将初始化Bean对象,此时会经过BeanPostProcessor方法对bean进行代理增强处理(例如依赖BeanPostProcessor实现事务,依赖注入等)。

相关文章:

Spring进阶篇(5)- BeanPostProcessor(Bean的后置处理器)
Spring进阶篇(10)-BeanPostProcessor的注册时机

Spring进阶篇(9)- MethodValidationPostProcessor 后置处理器的运用

那么能否在初始化Bean时,通过反射技术来实现属性的增强?

2.1 引入相关依赖

引入反射相关依赖:

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.10</version>
</dependency>

引入TTL依赖:目的是它会重写线程池,来实现ThreadLocal跨线程参数传递(目的为了测试)

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.11.5</version>
    <scope>compile</scope>
</dependency>

JAVA进阶篇(8)—TransmittableThreadLocal—父子线程间线程本地变量

从TransmittableThreadLocal使用前调研(源码分析)

2.2 代码实现

实现代码:本段代码是一个思路,细节还需单独处理。

@Slf4j
@Component
public class ExtendExecutorsBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {


    private int order = Ordered.LOWEST_PRECEDENCE - 1;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        //只处理本项目的bean
        if(bean.getClass().getName().contains("com.tellme")) {
            //反射得到的是ExecutorService、ThreadPoolExecutor的属性。
            Set<Field> allFields =
                    ReflectionUtils.getFields(bean.getClass(),
                            i -> i != null && (i.getType().equals(ExecutorService.class) || i.getType()
                                    .equals(ThreadPoolExecutor.class)));

            allFields.stream().forEach(f -> {
                f.setAccessible(true);
                try {
                    //获取类中属性
                    ThreadPoolExecutor executorService = (ThreadPoolExecutor) f.get(bean);
                    //对原有线程池的装饰,使用到TTL线程池(此处不能是ThreadPoolExecutor类,否则会转换不过去)
                    ExecutorService ttlExecutorService =
                            TtlExecutors.getTtlExecutorService(executorService);
                    //替换
                    f.set(bean, ttlExecutorService);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
        }
        return bean;
    }

    @Override
    public int getOrder() {
        return this.order;
    }
}

2.3 代码测试

@Slf4j
@RestController
public class ThreadLocalController {

    private ExecutorService executorService = new ThreadPoolExecutor(1, 2,
            60L, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>());

    public static TransmittableThreadLocal<User> local = new TransmittableThreadLocal<>();
    public static ThreadLocal<User> local2 = new ThreadLocal<>();


    @RequestMapping("/local/t1")
    public void t1(@RequestBody User user) {
        //第一次使用线程池时前,创建多个ThreadLocal的值。
        local.set(user);
        local2.set(user);
        System.out.println(executorService.getClass());
        executorService.execute(() -> {
            log.info("【/local/t1的add操作】子线程打印数据1:{}", local.get());
            log.info("【/local/t1的add操作】子线程打印数据2:{}", local2.get());
        });


    }


    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class User{
        private String id;

        private String name;
    }

}
上一篇下一篇

猜你喜欢

热点阅读