Bootstrap

Spring的@Async异步编程

使用@Async注解时,推荐使用自定义线程池的模式;查看源码,@Async的默认线程池为SimpleAsyncTaskExecutor,默认线程池有如下弊端:
在线程池应用中,参考阿里巴巴java开发规范:线程池不允许使用Executors去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确线程池的运行规则,规避资源耗尽的风险。

一、自定义异步线程池

package com.nijia.base.config;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

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

import lombok.extern.slf4j.Slf4j;

/**
 * 自定义线程池<br/><br/>
 * 
 * 使用@Async注解时,推荐使用自定义线程池的模式
 * @author Lynch
 *
 */
@EnableAsync
@Configuration
@Slf4j
public class TaskPoolConfig {
    /**
     * 核心线程数 自行设置
     */
    private static final int CORE_POOL_SIZE = 50;
    /**
     * 最大线程数 自行设置
     */
    private static final int MAX_POOL_SIZE = CORE_POOL_SIZE * 2 + 1;
      
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        //返回可用处理器的Java虚拟机的数量 16
        int i = Runtime.getRuntime().availableProcessors();
        log.info("系统最大线程数  : " + i);
        
        // ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        MyThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor();
        //设置核心线程数
        executor.setCorePoolSize(CORE_POOL_SIZE);
        //设置最大线程数
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        //配置队列容量,默认值为Integer.MAX_VALUE
        executor.setQueueCapacity(99999);
        //除核心线程外的线程存活时间
        executor.setKeepAliveSeconds(3);
        //线程名字前缀
        executor.setThreadNamePrefix("thread-execute");
        // 设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
        executor.setAwaitTerminationSeconds(60);
        //优雅地关闭线程池,等待所有的任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //设置拒绝策略
        //CallerRunsPolicy():交由调用方线程运行,比如 main 线程;如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行
        //AbortPolicy():该策略是线程池的默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
        //DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常
        //DiscardOldestPolicy():丢弃队列中最老的任务,队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

 

 

 

二、AsyncService

package cn.itcast.user.service;

/**   
 *    异步发送短信/邮件
 *
 * @author Lynch 
 */
public interface AsyncService {
    /**
     * 发送短信
     * 
     * @author Lynch
     */
    void sendSms();

    /**
     * 发送邮件
     * 
     * @author Lynch
     */
    void sendEmail();
}

三、使用@Async注解异步执行——AsyncServiceImpl

package cn.itcast.user.service.impl;

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

import cn.itcast.user.service.AsyncService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {

    @Override
    @Async("taskExecutor")
    public void sendSms() {
        try {
            Thread.sleep(3000);
            log.info("发送短信成功");
        } catch (Exception e) {
            log.error("发送短信异常 -> ", e);
        }
    }

    @Override
    @Async("taskExecutor")
    public void sendEmail() {
        try {
            Thread.sleep(2000);
            log.info("发送email成功");
        } catch (Exception e) {
            log.error("发送email异常 -> ", e);
        }
    }

}

四、单元测试——AsyncServiceTest

package cn.itcast.user.service;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import cn.itcast.user.BaseTest;

public class AsyncServiceTest extends BaseTest {

    @Autowired
    private AsyncService asyncService;
    
    
    @Test
    public void send() {
        long begin = System.currentTimeMillis();
        asyncService.sendSms();
        asyncService.sendEmail();
        System.out.println("执行耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
    }
}

 使用方法二:

package com.nijia.user.controller;

@RestController
@RequestMapping("/open")
public class UserDemoController {
    @Autowired
    private Executor taskExecutor;

    
    
    @GetMapping("executor")
    public String executor() throws Exception {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(400L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "111";
        }, taskExecutor);
        
        if(taskExecutor instanceof MyThreadPoolTaskExecutor) {
            System.out.println(((MyThreadPoolTaskExecutor) taskExecutor).getPoolSize());
            System.out.println(((MyThreadPoolTaskExecutor) taskExecutor).getMaxPoolSize());
            System.out.println(((MyThreadPoolTaskExecutor) taskExecutor).getCorePoolSize());
        }
        
        return future.get();
    }
    
     
}

 

;