@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了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM