美文网首页
SpringBoot多线程

SpringBoot多线程

作者: jeffrey_hjf | 来源:发表于2023-06-29 15:49 被阅读0次

    1 线程同步和异步

    线程同步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A只能等待下去。耗时较长,安全性较高。

    线程异步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程仍然请求的到。

    一个进程启动的多个不相干的进程,他们之间的相互关系为异步;同步必须执行到底后才能执行其他操作,异步可同时执行。

    多个线程执行的时候需要同步,如果是单线程则不需要同步。

    2 异步实例

    主方法和被调用的方法必须是不同的类,才能实现多线程。

    2.1 启动类

    使用@EnableAsync来开启 SpringBoot 对于异步任务的支持。

    Application:

    @SpringBootApplication
    @EnableAsync
    publicclass Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    2.2 线程池

    配置类实现接口AsyncConfigurator,返回一个ThreadPoolTaskExecutor线程池对象。

    config/AsyncConfig:

    @Configuration
    @EnableAsync
    publicclass AsyncConfig implements AsyncConfigurer {
    
        // ThredPoolTaskExcutor的处理流程
        // 当池子大小小于corePoolSize,就新建线程,并处理请求
        // 当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去workQueue中取任务并处理
        // 当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理
        // 当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁
    
        @Override
        @Bean
        public Executor getAsyncExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            // 核心线程数:线程池创建的时候初始化的线程数
            executor.setCorePoolSize(10);
            // 最大线程数:线程池最大的线程数,只有缓冲队列满了之后才会申请超过核心线程数的线程
            executor.setMaxPoolSize(100);
            // 缓冲队列:用来缓冲执行任务的队列
            executor.setQueueCapacity(50);
            // 线程池关闭:等待所有任务都完成再关闭
            executor.setWaitForTasksToCompleteOnShutdown(true);
            // 等待时间:等待5秒后强制停止
            executor.setAwaitTerminationSeconds(5);
            // 允许空闲时间:超过核心线程之外的线程到达60秒后会被销毁
            executor.setKeepAliveSeconds(60);
            // 线程名称前缀
            executor.setThreadNamePrefix("learn-Async-");
            // 初始化线程
            executor.initialize();
            return executor;
        }
    
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            returnnull;
        }
    }
    

    2.3 controller

    通过该层调用测试 Async。

    @RestController
    @RequestMapping("/homepage")
    publicclass AsyncController {
        @Autowired
        AsyncService asyncTaskService;
        @GetMapping("/learnAsync")
        public String learnAsync(){
            for (int i = 0; i < 10; i++) {
                asyncTaskService.executeAsyncTask(i);
            }
            return"1";
        }
    }
    

    2.4 service

    通过@Async注解表明该方法是异步方法,如果注解在类上,那表明这个类里面的所有方法都是异步的。

    @Service
    publicclass AsyncService {
        privatefinalstatic Logger logger = LoggerFactory.getLogger(com.spring.boot.service.AsyncService.class);
        @Async// 表明该方法是异步方法。如果注解在类上,那表明类里面的所有方法都是异步
        public void executeAsyncTask(int i) {
            logger.info("\t 完成任务" + i);
            System.out.println("线程" + Thread.currentThread().getName() + " 执行异步任务:" + i);
        }
    }
    

    2.5 输出

    image.png

    3 Future 类

    修改service层,分别使用同步调用、异步调用无返回、异步调用使用 Future 返回。

    3.1 同步调用

    public long subBySync() throws Exception {
        long start = System.currentTimeMillis();
        long sum = 0;
        long end = System.currentTimeMillis();
        sum = end - start;
        return sum;
    }
    

    3.2 异步调用无返回

    @Async
    public void subByVoid() throws Exception {
        long start = System.currentTimeMillis();
        long sum = 0;
        long end = System.currentTimeMillis();
        sum = end - start;
    }
    

    3.3 异步调用 Future 返回

    controller:

    Future<Long> task = asyncTaskService.subByAsync();
    

    service:

    @Async
    public Future<Long> subByAsync() throws Exception {
        long start = System.currentTimeMillis();
        long sum = 0;
        long end = System.currentTimeMillis();
        sum = end - start;
        returnnew AsyncResult<>(sum);
    }
    

    4 CompletableFuture 类

    若使用 Future 出现报错:

    无法判断org.springframework.scheduling.annotation.AsyncResult<>的类型参数

    不存在类型变量V的实例,使org.springframework.scheduling.annotation.AsyncResult符合XXX

    可以使用 CompletableFuture 类:

    @Asyncpublic
    CompletableFuture<Map<String, Object>> subByAsyncMap() throws Exception {
        Map<String, Object> res = new HashMap<>();
        return CompletableFuture.completedFuture(res);
    }
    

    5 线程关闭

    当线程数量超过核心线程数量之后,运行完毕的旧的线程会被关闭。

    可以通过定时任务测试。

    batch/ScheduledTaskService:

    @Component
    @EnableScheduling
    publicclass ScheduledTaskService {
        @Autowired
        AsyncService asyncService;
        @Scheduled(cron = "1/1 * * * * ? ")  //1s一次
        public void learnCron(){
            asyncService.learnScheduledAsync();
        }
    }
    

    在 AsyncService 添加方法:

    // 使用定时任务调用此方法创建线程
    @Async
    public void learnScheduledAsync(){
        Long timeLong = System.currentTimeMillis();
        SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //设置格式
        String timeString = timeFormat.format(timeLong);
        System.out.println("线程" + Thread.currentThread().getName());
        System.out.println("timeString:" + timeString + "\n");
    }
    

    在异步配置(AsyncConfig)中已设置核心线程数为10:

    // 核心线程数:线程池创建的时候初始化的线程数
    executor.setCorePoolSize(10);
    

    运行可以观察输出,线程数达到10后会再一次从1开始。

    作者:jeffrey_h
    链接:https://juejin.cn/spost/7250323822121566266
    来源:稀土掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

          本文标题:SpringBoot多线程

          本文链接:https://www.haomeiwen.com/subject/epaaydtx.html