Springboot中使用@Async开启异步功能

Springboot中使用@Async开启异步功能

何为异步?

同步:多个任务存在先后关系,必须按顺序一个接着一个任务执行

异步:当前任务在执行,后面的任务不必等当前任务完成才执行,可以同时执行

==简单来说:==在异步调用的过程中,一个任务的执行不会阻塞其他任务的执行,而是将任务提交给异步执行的机制,让程序能够继续执行其他操作。

能解决何问题?

异步能够解决许多并发并行的问题,特别是在处理一些耗时的操作时,能够提高程序的效率和性能。

常用的场景有:

  1. 网络请求:在进行网络请求时,通常需要等待服务器响应。如果使用同步调用,程序会阻塞等待响应返回,影响程序的响应性和并发性。使用异步调用,可以在等待响应的同时执行其他任务,从而提高程序的效率和性能。
  2. 文件读写:在进行文件读写时,通常需要等待文件的打开、读取或写入操作完成。如果使用同步调用,程序会阻塞等待操作完成,影响程序的响应性和并发性。使用异步调用,可以在等待文件操作的同时执行其他任务,从而提高程序的效率和性能。
  3. 数据库查询:在进行数据库查询时,通常需要等待查询结果返回。如果使用同步调用,程序会阻塞等待查询结果,影响程序的响应性和并发性。使用异步调用,可以在等待查询结果的同时执行其他任务,从而提高程序的效率和性能。

Springboot实现异步

通过在方法上面加上==@Async注解,表示该方法开实现了异步调用,在主启动类上面加上@EnableAsync==来开启异步功能

第一步:在主启动类上面加上@EnableAsync注解

@EnableAsync
@SpringBootApplication
public class AsyncDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(AsyncDemoApplication.class, args);
    }

}

第二步:在对应想要使用异步的方法上面加上注解@Async

@Service
public class UserServiceImpl {
    @Async
    public void asyncMethod() {
        System.out.println("当前线程名字为:" + Thread.currentThread().getName());
    }
}

第三步:测试

    @Resource
    private UserServiceImpl userService;

    @Test
    void contextLoads() {
        userService.asyncMethod();
        //当前线程名字为:task-1
    }

多次调用方法,查看线程名字,可以看到使用了多线程

    @Test
    void contextLoads() {
        for(int i = 0; i <= 1000; i++) {
            userService.asyncMethod();
//            当前线程名字为:task-2
//            当前线程名字为:task-2
//            当前线程名字为:task-2
//            当前线程名字为:task-2
//            当前线程名字为:task-2
//            当前线程名字为:task-1
//            当前线程名字为:task-4
//            当前线程名字为:task-4
//            当前线程名字为:task-4
//            当前线程名字为:task-5
//            当前线程名字为:task-6
//            当前线程名字为:task-7
//            当前线程名字为:task-6
        }
    }

image-20231026233535361

可以看到我们的线程数量增多以及cpu的利用率增高

自定义线程池代替默认线程池

自定义线程池可以根据实际需求和场景来优化线程池的行为,从而更好地满足应用程序的性能和资源管理需求。

主要有以下三个方面:

  1. 线程数量控制:默认线程池通常使用固定数量的线程,但在某些情况下,这可能不是最优的选择。自定义线程池可以根据实际需求动态地调整线程数量,以达到最佳的性能和资源利用率。
  2. 任务队列管理:默认线程池通常使用无界队列或有界队列来管理待执行的任务。然而,这可能会导致内存溢出或任务积压的问题。自定义线程池可以根据实际需求选择合适的任务队列类型,并设置合理的队列大小或拒绝策略,以避免资源耗尽或任务丢失的情况。
  3. 线程池参数调优:默认线程池的参数(如核心线程数、最大线程数、线程空闲时间等)可能无法满足特定应用程序的需求。自定义线程池可以根据实际情况进行参数调优,以提高线程池的性能和效率。例如,通过调整核心线程数和最大线程数的比例,可以在任务负载较轻时减少线程的创建和销毁开销。

==总之:==自定义线程池可以根据实际需求和场景来优化线程池的行为,以提高应用程序的性能、资源利用率和可管理性。通过灵活配置线程池的参数、任务队列和监控机制,可以更好地满足特定应用程序的需求,并提供更好的用户体验。

自定义线程池:

/**
 * 定义异步任务执行线程池
 * */
@Configuration
public class TaskPoolConfig {
    @Bean("taskExecutor")
    public Executor taskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        // 核心线程数10:线程池创建时候初始化的线程数
        executor.setCorePoolSize(10);
        // 最大线程数20:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(15);
        // 缓冲队列200:用来缓冲执行任务的队列
        executor.setQueueCapacity(200);
        // 允许线程的空闲时间60秒:当超过了核心线程数之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        // 线程池名的前缀:设置好了之后可以方便定位处理任务所在的线程池
        executor.setThreadNamePrefix("taskExecutor-");
        /*
        线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,
        当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;
        如果执行程序已关闭,则会丢弃该任务
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }
}

测试:

    @Async("taskExecutor")
    public void asyncMethod() throws InterruptedException {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {

        }
        Thread.sleep(2000);
        long end = System.currentTimeMillis();
        System.out.println("线程 " + Thread.currentThread().getName() + "\t耗时:" + (end - start));
    }
    @Test
    void contextLoads() throws InterruptedException {
        userService.asyncMethod();
        //线程 taskExecutor-1	耗时:2017
    }
}

```java
    @Test
    void contextLoads() throws InterruptedException {
        userService.asyncMethod();
        //线程 taskExecutor-1	耗时:2017
    }

感谢观看,如有不对,还请多多指正(抱拳)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值