Java并发编程 之SpringBoot线程池异步开发

SpringBoot调用方法异步执行

自定义线程池:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务;
 * 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
 * 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝;
 */
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
    /**
     * 核心线程数(默认线程数)
     */
    private static final int corePoolSize = 20;
    /**
     * 最大线程数
     */
    private static final int maxPoolSize = 100;
    /**
     * 允许线程空闲时间(单位:默认为秒)
     */
    private static final int keepAliveTime = 10;
    /**
     * 缓冲队列大小
     */
    private static final int queueCapacity = 200;
    /**
     * 线程池名前缀
     */
    private static final String threadNamePrefix = "Async-Service-";

    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveTime);
        executor.setThreadNamePrefix(threadNamePrefix);
        // 线程池对拒绝任务的处理策略
        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 初始化
        executor.initialize();
        return executor;
    }
}

方法异步执行:
代码中的 @Async(“taskExecutor”) 对应我们自定义线程池中的 @Bean(“taskExecutor”) ,表示使用我们自定义的线程池。

@Async("taskExecutor")  

如下方式会使@Async失效:
1、异步方法使用 static 修饰。
2、异步类没有被 Spring 容器管理。
3、异步方法不能与被调用的异步方法在同一个类中。
4、SpringBoot框架必须在配置类中增加 @EnableAsync 注解。

异步失效场景一:
testAsync2方法并不会进行异步调用,因为与被调用的方法在同一个类中。

@Slf4j
@Component
public class DemoService {

    @Async("taskExecutor")
    public void testAsync() {
        log.info("testAsync");
       testAsync2();
    }

    @Async("taskExecutor")
    public void testAsync2() {
        log.info("testAsync2");
    }
}

代码编写

扩展ThreadPoolTaskExecutor:
每次提交线程的时候都会将当前线程池的运行状况打印出来。

public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);

    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if (null == threadPoolExecutor) {
            return;
        }

        logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                // 线程名称前缀
                this.getThreadNamePrefix(),
                prefix,
                // 任务总数
                threadPoolExecutor.getTaskCount(),
                // 已完成数
                threadPoolExecutor.getCompletedTaskCount(),
                // 活跃线程数
                threadPoolExecutor.getActiveCount(),
                // 队列大小
                threadPoolExecutor.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }
}

线程池配置:

@Configuration
@EnableAsync
public class ExecutorConfig {
    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

    @Bean
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        // 配置核心线程数
        executor.setCorePoolSize(5);
        // 配置最大线程数
        executor.setMaxPoolSize(5);
        // 配置队列大小
        executor.setQueueCapacity(99999);
        // 配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("async-service-");

        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }

}

业务逻辑代码:
将Service层的服务异步化,使用 @Async(“asyncServiceExecutor”),表明executeAsync方法进入线程池运行。

public interface AsyncService {
    // 执行异步任务
    void executeAsync();
}

@Service
public class AsyncServiceImpl implements AsyncService {

    private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);
    
    @Async("asyncServiceExecutor")
    @Override
    public void executeAsync() {
        logger.info("start executeAsync");
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        logger.info("end executeAsync");
    }
}

Http服务:

@RestController
public class DemoController {

    private static final Logger logger = LoggerFactory.getLogger(DemoController.class);

    @Autowired
    private AsyncService asyncService;

    @GetMapping("/demo")
    public String demo() {
        logger.info("start submit");

        //调用service层的任务
        asyncService.executeAsync();

        logger.info("end submit");
        return "demo";
    }
}

测试:

2020-09-25 17:52:47.400  INFO 6552 --- [async-service-1] com.example.service.AsyncServiceImpl     : start executeAsync
2020-09-25 17:52:47.495  INFO 6552 --- [nio-8080-exec-1] com.example.controller.DemoController    : start submit
2020-09-25 17:52:47.495  INFO 6552 --- [nio-8080-exec-1] c.e.c.VisiableThreadPoolTaskExecutor     : async-service-, 2. do submit,taskCount [34], completedTaskCount [26], activeCount [5], queueSize [3]
2020-09-25 17:52:47.495  INFO 6552 --- [nio-8080-exec-1] com.example.controller.DemoController    : end submit
2020-09-25 17:52:47.559  INFO 6552 --- [async-service-2] com.example.service.AsyncServiceImpl     : end executeAsync
2020-09-25 17:52:47.559  INFO 6552 --- [async-service-2] com.example.service.AsyncServiceImpl     : start executeAsync
2020-09-25 17:52:47.658  INFO 6552 --- [nio-8080-exec-3] com.example.controller.DemoController    : start submit
2020-09-25 17:52:47.658  INFO 6552 --- [nio-8080-exec-3] c.e.c.VisiableThreadPoolTaskExecutor     : async-service-, 2. do submit,taskCount [35], completedTaskCount [27], activeCount [5], queueSize [3]
2020-09-25 17:52:47.658  INFO 6552 --- [nio-8080-exec-3] com.example.controller.DemoController    : end submit

注意这一行日志: 2. do submit,taskCount [35], completedTaskCount [27], activeCount [5], queueSize [3]
说明提交任务到线程池的时候,调用的是submit(Callable task)这个方法,当前已经提交了35个任务,完成了27个,当前有5个线程在处理任务,还剩3个任务在队列中等待,线程池的基本情况一路了然。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值