SpringBoot中使用线程池

1.配置线程池

使用springboot配置线程池,采用@EnableAsync和@Configuration注解,配置代码如下

@EnableAsync
@Configuration
public class SpringThreadPoolConfig {

    @Value("${mythreadpool.maxPoolSize}")
    private Integer maxPoolSize;
    @Value("${mythreadpool.corePoolSize}")
    private Integer corePoolSize;
    @Value("${mythreadpool.queueCapacity}")
    private Integer queueCapacity;
    @Value("${mythreadpool.keepAliveSeconds}")
    private Integer keepAliveSeconds;
    @Value("${mythreadpool.threadNamePrefix}")
    private String threadNamePrefix;
    @Value("${mythreadpool.waitForTasksToCompleteOnShutdown}")
    private Boolean waitForTasksToCompleteOnShutdown;

    @Bean("taskExecutor")
    public Executor asyncServiceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数等于系统核数--8核
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        executor.setCorePoolSize(availableProcessors);
        // 设置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 线程满了之后由调用者所在的线程来执行
        // 拒绝策略:CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置默认线程名称
        executor.setThreadNamePrefix(threadNamePrefix);
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown);
        //执行初始化
        executor.initialize();
        return executor;
    }
}

线程池的参数配置在application-dev.yml中,具体如下

mythreadpool:
  maxPoolSize: 20
  corePoolSize: 8
  queueCapacity: 2048
  keepAliveSeconds: 60
  threadNamePrefix: springThreadPool
  waitForTasksToCompleteOnShutdown: true

2.创建异步调用接口以及实现类

 这里主要是使用到@Async("XXX")这个注解来声明使用XXX线程池异步执行该方法,代码如下

public interface AsyncService {
    public String doMethod(String message);
}
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
    @Async("taskExecutor")
    @Override
    public String doMethod(String message) {
        String fileName ="C://EasyExcel.xlsx";
        EasyExcel.read(fileName, DemoData.class, new DemoDatalistener()).sheet().doRead();
        return message;
    }
}

@RestController
public class AsyncController {

    @Autowired
    AsyncService asyncService;

    @GetMapping("/sync/testMethod")
    public String testMethod() {
        Instant beginTime = Instant.now();
        asyncService.doMethod("message");
        Instant endTime = Instant.now();
        System.out.println("时间差(毫秒):"+ Duration.between(beginTime, endTime).toMillis());
        return "success";
    }
}

3.业务逻辑代码

为了展示使用线程的好处,直观的对比线程池使用前后方法耗时时间,加入了一些业务的处理。业务假设为使用easyexcel读取文件,其中相关代码如下

@Data
public class DemoData {
    @ExcelProperty("商品名称")
    private String name;
    @ExcelProperty("日期")
    private Date date;
    @ExcelProperty("金额")
    private Double doubleData;
}
public class DemoDatalistener extends AnalysisEventListener<DemoData> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDatalistener.class);
    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    List<DemoData> list = new ArrayList<DemoData>();
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private DemoSeviceImpl demoSeviceImpl;
    public DemoDatalistener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoSeviceImpl = new DemoSeviceImpl();
    }
    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoSeviceImpl
     */
    public DemoDatalistener(DemoSeviceImpl demoSeviceImpl) {
        this.demoSeviceImpl = demoSeviceImpl;
    }

    @Override
    public void invoke(DemoData demoData, AnalysisContext analysisContext) {
        //System.out.println(JSON.toJSONString(demoData));
        list.add(demoData);
        //System.out.println(list);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();//自定义的业务
            // 存储完成清理 list
            list.clear();
        }
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        LOGGER.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        demoSeviceImpl.save(list);
    }
}
public interface DemoService {
    public void save(List<DemoData> list);
}
@Service
public class DemoSeviceImpl implements DemoService {

    @Override
    public void save(List<DemoData> list) {

    }
}

4.执行结果

 在第三步中,对@Async注解进行注释前后对比

 //@Async("taskExecutor")

 在浏览器访问开5个窗口访问,其前后结果如下

注释前:

时间差(毫秒):3
时间差(毫秒):1
时间差(毫秒):0
时间差(毫秒):1
时间差(毫秒):1
2022-04-20 15:16:12.490  INFO 23932 --- [ringThreadPool1] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
2022-04-20 15:16:51.197  INFO 23932 --- [ringThreadPool2] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
2022-04-20 15:16:52.980  INFO 23932 --- [ringThreadPool3] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
2022-04-20 15:16:54.640  INFO 23932 --- [ringThreadPool4] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
2022-04-20 15:16:55.602  INFO 23932 --- [ringThreadPool5] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!

注释后:

2022-04-20 16:04:02.422  INFO 19832 --- [nio-8082-exec-1] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
时间差(毫秒):46973
2022-04-20 16:04:39.058  INFO 19832 --- [nio-8082-exec-2] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
时间差(毫秒):62450
2022-04-20 16:04:40.914  INFO 19832 --- [nio-8082-exec-3] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
时间差(毫秒):62802
2022-04-20 16:04:42.504  INFO 19832 --- [nio-8082-exec-4] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
时间差(毫秒):62586
2022-04-20 16:04:43.373  INFO 19832 --- [nio-8082-exec-5] c.r.demo.listener.DemoDatalistener       : 所有数据解析完成!
时间差(毫秒):61924

  • 10
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在Spring Boot创建线程池也有多种方式,其一种常用的方式是通过配置文件来创建。 首先,在Spring Boot应用程序的配置文件添加以下配置: ``` spring: task: execution: pool: core-size: 10 max-size: 20 queue-capacity: 200 ``` 上述配置创建了一个线程池,其包含10个核心线程,最多可以有20个线程,队列容量为200。这些值可以根据您的实际需求进行设置。 然后,在需要使用线程池的地方,可以使用Spring框架提供的TaskExecutor接口来使用线程池。例如: ``` import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.task.TaskExecutor; import org.springframework.stereotype.Component; @Component public class MyService { @Autowired private TaskExecutor taskExecutor; public void myMethod() { taskExecutor.execute(new Runnable() { public void run() { // 需要异步执行的代码逻辑 } }); } } ``` 在上面的示例代码,MyService类的myMethod()方法使用TaskExecutor接口来执行异步任务。在实际应用,可以根据需要使用不同的TaskExecutor实现类来创建不同的线程池。 除了通过配置文件来创建线程池,还可以在Java代码手动创建线程池。例如: ``` import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.springframework.stereotype.Component; @Component public class MyService { private ExecutorService executorService = Executors.newFixedThreadPool(10); public void myMethod() { executorService.submit(new Runnable() { public void run() { // 需要异步执行的代码逻辑 } }); } } ``` 在上面的示例代码,MyService类的myMethod()方法手动创建了一个包含10个线程的线程池,并使用submit()方法来执行异步任务。 希望这个回答能够对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值