🙏废话不多说系列,直接开干🙏
一、什么是异步任务?
异步任务(Asynchronous Task)是一种在程序执行过程中,不等待某个特定操作完成就继续执行后续操作的任务处理方式。与同步任务(Synchronous Task)不同,异步任务不会阻塞主线程或调用线程,而是允许其他任务或操作在等待特定操作完成的同时继续执行。
在异步任务中,当发起一个需要时间的操作时(如网络请求、文件读写、复杂计算等),程序不会等待该操作完成,而是立即返回并继续执行后续的代码。当该操作完成时,通常会通过回调函数、事件监听、Promise对象或其他机制来通知程序,以便进行相应的处理。
异步任务在编程中非常常见,尤其在处理I/O操作、并发请求或需要保持程序响应性的场景中。通过使用异步任务,可以提高程序的性能和响应速度,避免长时间阻塞主线程,从而提供更好的用户体验。
需要注意的是,异步任务的处理方式可能会因编程语言和框架的不同而有所差异。例如,在JavaScript中,可以使用回调函数、Promises或async/await来处理异步任务;而在其他语言中,可能使用线程、协程或其他并发机制来实现异步处理。
二、使用 Java8 的 Future 实现异步任务
(1)完整源码
// 第一种(创建线程池,不建议这么写)
public Object javaPromise() {
// 两个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
//jdk1.8之前的实现方式
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("task started!");
//模拟耗时操作
asynchronousService.longTimeMethod();
return "task finished!";
}, executor);
//采用lambada的实现方式
future.thenAccept(e -> System.out.println(e + " ok"));
System.out.println("main thread is running");
return "java8Promise";
}
// 第二种:手动创建线程池
private Object defineCreateThreadPool() {
//获取系统处理器个数,作为线程池数量
int nThreads = Runtime.getRuntime().availableProcessors();
System.out.println("当前系统处理器个数,作为线程池数量:" + nThreads);
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024),
namedThreadFactory,
new ThreadPoolExecutor.AbortPolicy()
);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("task started!");
//模拟耗时操作
asynchronousService.longTimeMethod();
return "task finished!";
}, pool);
future.thenAccept(e -> System.out.println(e + "ok"));
System.out.println("The Thread is running");
return "java8Promise";
}
解析:
异步任务,将超长耗时的任务一部执行,先执行非异步的数据。
// 先输出
main thread is running
task started!
// 等到耗时长的任务再输出
82
task finished! ok
三、Spring Boot 实现 异步任务
(1)启动类上添加注解 @EnableAsync
import org.springframework.scheduling.annotation.EnableAsync;
// 启动类 添加 注解 @EnableAsync
@EnableAsync
@MapperScan(value = {"com.deelon.data.transfer.dao"})
@SpringBootApplication
public class TransferApplication {
public static void main(String[] args) {
SpringApplication.run(TransferApplication.class, args);
}
}
(2)异步任务层( AsyncService.java)
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
// 异步任务 SERVICE 层(实际应用中,可以替换替换相关接口,重点在 添加异步标识的注解@Async)
@Component
public class Print {
// 这个 myThread 标识指定多线程的配置
@Async("myThread")
public void print() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("--- async: thread name: " + Thread.currentThread().getName());
}
}
(3)自定义多线程配置(自定义线程池)
① application.properties 中添加
## 线程池
spring.task.execution.pool.core-threads=3
spring.task.execution.pool.max-threads=5
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10
② 书写 配置类 ThreadsConfig.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
// 多线程配置
@Configuration
public class ThreadsConfig implements AsyncConfigurer {
@Value("${spring.task.execution.pool.core-threads}")
private int corePoolSize;
@Value("${spring.task.execution.pool.max-threads}")
private int maxPoolSize;
@Value("${spring.task.execution.pool.queue-capacity}")
private int queueCapacity;
@Value("${spring.task.execution.pool.keep-alive}")
private int keepAliveSeconds;
@Override
@Bean("myThread")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
//设置核心线程数
threadPool.setCorePoolSize(corePoolSize);
//设置最大线程数
threadPool.setMaxPoolSize(maxPoolSize);
//线程池所使用的缓冲队列
threadPool.setQueueCapacity(queueCapacity);
//等待任务在关机时完成--表明等待所有线程执行完
threadPool.setWaitForTasksToCompleteOnShutdown(true);
//线程池的工作线程空闲后(指大于核心又小于max的那部分线程),保持存活的时间
threadPool.setAwaitTerminationSeconds(keepAliveSeconds);
//饱和策略默认是直接抛弃任务
//初始化线程
threadPool.initialize();
return threadPool;
}
}
(4)调用异步任务的业务层
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author zero
* @create 2021-04-09 23:16
*/
@Service
public class AsyncDemo {
@Resource
private Print print;
public void test() {
//异步方法
for (int i = 0; i < 10; i++) {
print.print();
//打印当前线程
System.out.println("---main: thread name: " + Thread.currentThread().getName());
if (i == 9) {
System.out.println("This is end of Main ====== END");
}
}
}
}
(5)书写测试Controller类
@RestController
@RequestMapping(value = "/async")
public class TestAsynchronousController {
@Resource
private AsyncDemo asyncDemo;
@GetMapping("/spring")
public Object springAsync() {
asyncDemo.test();
return "async success";
}
}
(6)相关【坑】积累 ♥
异步任务失效的原因
- 没有在 @SpringBootApplication 启动类中添加注解 @EnableAsync 注解;
- 异步方法使用注解 @Async 的返回值只能为 void 或者 Future;
- 没有走 Spring 的代理类。(因为 @Transactional 和 @Async 注解的实现都是基于 Spring 的 AOP 的实现,是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能调用方法的是对象本身而不是代理对象,因为没有经过 Spring 容器。)
🙏至此,非常感谢阅读🙏