Spring:异步线程池和定时器

一、异步线程池

在Spring中存在一个AsyncConfigurer接口,它是一个可以配置异步线程池的接口,它的源码如下:

package org.springframework.scheduling.annotation;

import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.lang.Nullable;

public interface AsyncConfigurer {
    //获取线程池
    @Nullable
    default Executor getAsyncExecutor() {
        return null;
    }

    //异步异常处理器
    @Nullable
    default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

       其中getAsyncExecutor方法返回的是一个自定义的线程池,这样在开启异步时,线程池就会提供空闲线程来执行异步任务。因为线程中的业务逻辑可能抛出异常,所以还有一个处理异常的处理器方法,使得异常可以自定义处理。

下面看看如何配置使用异步线程池。

1、首先开发一个Java配置文件,实现AsyncConfigurer接口,并添加@EnableAsync注解,开启异步功能。

package com.scb.mongodemo.config;

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

import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        //定义线程池
        ThreadPoolTaskExecutor taskExecutor=new ThreadPoolTaskExecutor();
        //核心线程数
        taskExecutor.setCorePoolSize(10);
        //线程池最大线程数
        taskExecutor.setMaxPoolSize(30);
        //线程队列最大线程数
        taskExecutor.setQueueCapacity(2000);
        //设置线程前缀
        taskExecutor.setThreadNamePrefix("async-executor-");
        //初始化
        taskExecutor.initialize();
        return taskExecutor;
    }
}

除了上诉的设置外,我们还能设置线程池的拒绝策略。如:

taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

拒绝策略常用的有以下四种: 

  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常(默认)。
  2. ThreadPoolExecutor.DiscardPolic:丢弃任务,但是不抛出异常。
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务
  4. ThreadPoolExecutor.CallerRunsPolic:由调用线程处理该任务

在上诉我们定义线程池时,使用的是ThreadPoolTaskExecutor,而spring异步线程池的接口类,其实质是Java.util.concurrent.Executor

Spring 已经实现的异常线程池:

1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。 
2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方 
3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类 
4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类 
5. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装

2、为了进行测试,定义一个异步服务接口

package com.scb.mongodemo.service;

public interface IAsyncService {
    public void generateReport();
}

然后就是它的实现类

package com.scb.mongodemo.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncServiceImpl implements IAsyncService {
    @Override
    @Async
    public void generateReport() {
        System.out.println("report thread name:"+"["+Thread.currentThread().getName()+"]");
    }
}
generateReport方法使用了@Async注解进行标注,这样在Spring的调用中,它就会使用线程池的线程去执行它。

3、异步控制器

package com.scb.mongodemo.controller;

import com.scb.mongodemo.service.IAsyncService;
import com.scb.mongodemo.service.ScheduleServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/async")
public class AsyncController {
    @Autowired
    private IAsyncService asyncService;

    @GetMapping("/page")
    public String asyncPage(){
        System.out.println("request thread name:"+"["+Thread.currentThread().getName()+"]");
        asyncService.generateReport();
        return "async";
    }
}

4、运行程序,访问http://localhost:8080/async/page,结果截图如下:

二、定时器

       在企业的实践生产中,可能需要使用一些定时任务。例如,在月末、季末和年末需要统计各种各样的报表,月表需要月末跑批量生成,年表需要年末跑批量生成,这样就需要制定不同的定时任务。

       在Spring中使用定时器是比较简单的,首先在配置文件中加入@EnableScheduling注解,就能够使用注解驱动定时任务的机制,接着就可以通过@Scheduled注解去使用定时器。

1、在SpringBoot启动类上标注@EnableScheduling注解,启动定时器功能。

2、开发一个服务类,进行测试使用

package com.scb.mongodemo.service;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class ScheduleServiceImpl {
    int count=1;

    @Scheduled(fixedRate = 1000)
    public void job(){
        System.out.println("["+Thread.currentThread().getName()+"]"+"->[job] run:"+count);
        count++;
    }
}

3、运行程序,结果截图如下:

上诉我们使用了@Scheduled的fixedRate参数去设置它每隔1秒便执行一次,除了fixedRate参数之外,@Scheduled注解还有以下参数:

@Scheduled注解各参数详解

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值