Spring异步机制:@Async

发布时间 2023-11-19 16:14:36作者: 时空穿越者

概述

当一个方法标注@Async注解时,该方法的调用将异步发生;这意味着调用者将在调用后立即返回,方法的实际执行将发生在提交给Spring TaskExecutor的任务中。

示例

示例1

使用@EnableAsync注解启用异步机制

@EnableAsync
@Configuration
public class AsyncConfig {
}

 

@Service
public class AsyncJob {
    @Async
    public void asyncMethodWithVoidReturnType() {
        System.out.println("Execute method asynchronously: start."
                + Thread.currentThread().getName());

        try {
            Thread.sleep(10 * 1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Execute method asynchronously: end."
                + Thread.currentThread().getName());
    }
}

 

@RestController
@RequestMapping("/home")
public class HomeController {

    @Autowired
    private AsyncJob asyncJob;

    @GetMapping("/user-info")
    public String queryUserName() {
        asyncJob.asyncMethodWithVoidReturnType();

        return "test - user";
    }
}

 

运行结果

Postman

 

日志

 

从上述日志中线程的名称可以知道,本示例中异步执行方法所用的线程池为Spring boot默认配置的线程池(TaskExecutionAutoConfiguration)

 

示例2

自定义线程池

@Configuration
public class ThreadPoolConfig {
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数
        poolExecutor.setCorePoolSize(1);
        // 最大线程数
        poolExecutor.setMaxPoolSize(3);
        // 队列大小
        poolExecutor.setQueueCapacity(5);
        // 线程最大空闲时间
        poolExecutor.setKeepAliveSeconds(60);
        // 拒绝策略
        poolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 线程名称前缀
        poolExecutor.setThreadNamePrefix("sendMessage-");

        return poolExecutor;
    }
}

 

@Service
public class AsyncJob {
    @Async("taskExecutor")
    public void asyncMethodWithVoidReturnType() {
        System.out.println("Execute method asynchronously: start."
                + Thread.currentThread().getName());

        try {
            Thread.sleep(10 * 1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Execute method asynchronously: end."
                + Thread.currentThread().getName());
    }
}

 

public class AsyncJobTest extends BaseTest {
    @Autowired
    private AsyncJob asyncJob;

    @Test
    void check_async() {
        System.out.println("check_async. "
                + Thread.currentThread().getName());

        asyncJob.asyncMethodWithVoidReturnType();

        try {
            Thread.sleep(30 * 1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

运行结果

 

特别说明

如果项目中配置了多个线程池,在@Async注解中必须指明具体的线程池,否则会有如下类似报错

(1)多个线程池,且其中某个线程池的名称为taskExecutor

 

本质原因:ThreadPoolTaskScheduler、ThreadPoolTaskExecutor都是org.springframework.core.task.TaskExecutor的子类

 

(2)多个线程池,且任何线程池的名称均不为taskExecutor

 

此时将使用默认构建的线程池(即SimpleAsyncTaskExecutor的实例)异步执行方法

具体细节见下一章

 

揭秘 - 线程池

(1)通过@Async注解指定线程池的秘密

 

 

 

(2)使用Spring boot默认线程池(即:taskExecutor) 或者 SimpleAsyncTaskExecutor 的秘密

 

 

 

Future

如果@Async方法需要返回运行结果,可声明方法返回值为Future类型:

A、可以声明为常规的Java .util.concurrent. future类型

B、可以声明为Spring的org.springframework.util.concurrent.ListenableFuture类型

C、从Spring 4.2开始也可以声明为Java中的CompletableFuture

@Service
public class AsyncJob {

  @Async
  public CompletableFuture<Integer> getEmployeeCount() {
    int count = 0;
    // ...
    return CompletableFuture.completedFuture(count);
  }
}

 

扩展阅读

How To Do @Async in Spring | Baeldung