@Async失效之谜

@Async如何使用

  • 异步的方法上加上@Async异步注解
  • 启动类中需要加上@EnableAsync才有效
    使用时类似于下列函数:
new Thread(()-> System.out.println("hello world !"))

@Async线程池

  • 默认线程池
    无论重复多少次,都默认8个左右的线程在跑
    异步线程:task-1执行成功
    异步线程:task-2执行成功
    异步线程:task-3执行成功
    异步线程:task-4执行成功
    异步线程:task-5执行成功
    异步线程:task-6执行成功
    异步线程:task-7执行成功
    异步线程:task-1执行成功
    异步线程:task-2执行成功
    异步线程:task-8执行成功
    异步线程:task-3执行成功
    异步线程:task-8执行成功
    异步线程:task-6执行成功
    异步线程:task-8执行成功
    异步线程:task-5执行成功
    异步线程:task-3执行成功
    异步线程:task-2执行成功
    异步线程:task-1执行成功
  • 自定义线程池配置
/**
 * 为Async配置自定义线程池
 * 可存放的最多线程数为:MAX_POOL_SIZE+QUEUE_CAPACITY,同时进入线程池的数量超过这个就会报错
 * 线程名称最多为MAX_POOL_SIZE个
 */
@Configuration
public class MyTaskExecutorConfig implements AsyncConfigurer {

    /**
     * 设置ThreadPoolExecutor的核心池大小。
     */
    private static final int CORE_POOL_SIZE = 2;
    /**
     * 设置ThreadPoolExecutor的最大池大小。
     */
    private static final int MAX_POOL_SIZE = 3;
    /**
     * 设置ThreadPoolExecutor的BlockingQueue的容量。
     */
    private static final int QUEUE_CAPACITY = 5;

    /**
     * 配置类实现AsyncConfigurer接口并重写getAsyncExcutor方法,并返回一个ThreadPoolTaskExevutor
     * 这样我们就获得了一个基于线程池的TaskExecutor
     */
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

异步线程:ThreadPoolTaskExecutor-2执行成功
异步线程:ThreadPoolTaskExecutor-1执行成功
异步线程:ThreadPoolTaskExecutor-3执行成功
异步线程:ThreadPoolTaskExecutor-1执行成功
异步线程:ThreadPoolTaskExecutor-3执行成功
异步线程:ThreadPoolTaskExecutor-2执行成功
异步线程:ThreadPoolTaskExecutor-1执行成功
异步线程:ThreadPoolTaskExecutor-3执行成功

@Async和@Controller同时使用存在异常

AbstractHandlerMethodMapping初始bean时,在afterPropertiesSet方法中initHandlerMethods()关联我们的SpringMVCBean

// 类型判断
if (beanType != null && isHandler(beanType)) {
        // url处理
	detectHandlerMethods(beanName);
}

// Controller和RequestMapping注解判断
protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

a、@Controller不使用@Async注解时

@RestController
@Slf4j
public class MemberServiceImpl1 {

    @GetMapping("/addUser1")
    public String addUser() {
        log.info(">>>流程1");
        log.info(">>>流程2");
        return "success";
    }
}

image.png

b、@Controller使用@Async注解,但不继承接口时,异步失效

@RestController
@Slf4j
public class MemberServiceImpl3 {

    @GetMapping("/addUser3")
    public String addUser() {
        log.info(">>>流程1");
        addUserLog();
        log.info(">>>流程3");
        return "success";
    }

    @Async()
    public String addUserLog() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }
        log.info(">>>流程2");
        return "success";
    }
}

image.png
异步失效

c、@Controller使用@Async注解,同时继承接口

@RestController
@Slf4j
public class MemberServiceImpl4 implements MemberService {

    @Override
    @GetMapping("/addUser4")
    public String addUser() {
        log.info(">>>流程1");
        addUserLog();
        log.info(">>>流程3");
        return "success";
    }

    @Async()
    public String addUserLog() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }
        log.info(">>>流程2");
        return "success";
    }
}

image.png
访问404

@Async使用建议

  • a、异步执行的建议单独开启一个类实现,或者从容器中直接获取到该代理类后执行
/** 
* 异步代码写到别的地方,不要和Controller注解同时使用
*/
@RestController
@Slf4j
public class ResolveServiceImpl {

    @Autowired
    private MemberServiceManage memberServiceManage;

    @GetMapping("/resolve2")
    public String addUser() {
        log.info(">>>流程1");
        memberServiceManage.addUserLog();
        log.info(">>>流程3");
        return "success";
    }
}
@Component
@Slf4j
public class MemberServiceManage {
    @Async
    public String addUserLog() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }
        log.info(">>>流程2");
        return "success";
    }
}
/** 
* 使用SpringUtils获得bean后调用,不要使用this操作
*/
@RestController
@Slf4j
public class ResolveServiceImpl {

    @GetMapping("/resolve1")
    public String addUser() {
        log.info(">>>流程1");
        //把直接调用改为从容器中取一次
        ResolveServiceImpl bean = SpringUtils.getBean(ResolveServiceImpl.class);
        bean.addUserLog();
        log.info(">>>流程3");
        return "success";
    }

    @Async()
    public String addUserLog() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }
        log.info(">>>流程2");
        return "success";
    }
}
  • b、异步的方法上不要加static,加了static就不走AOP了
原文地址:https://www.cnblogs.com/jarye/p/13962045.html