《SpringBoot从入门到放弃》之第(十三)篇——使用@Async异步调用,ThreadPoolTaskScheduler线程池,使用Future以及定义超时

本篇博客是在上一篇《SpringBoot从入门到放弃》之第(十二)篇——使用@Async实现异步调用的基础上继续的。

创建 TaskPoolConfig 类,配置线程池:

package com.test.util;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

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

/**
 * 线程池配置
 */
@Configuration
public class TaskPoolConfig {

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

executor.setWaitForTasksToCompleteOnShutdown(true);//用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
executor.setAwaitTerminationSeconds(60);//该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。

上面的两个方法能安全的关闭线程池。

 

如何使用线程池呢?

很简单,在 @Async 注解里标注线程池的名称即可:@Async("myTaskExecutor")

我们在每个方法里都加上一句话,打印线程名:System.out.println("当前线程名:"+ Thread.currentThread());

package com.test.web;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.concurrent.Future;

@Component
public class LoadMessageTest {

    /**
     * 加载主页
     *
     * @throws Exception
     */
    @Async("myTaskExecutor")
    public Future<Boolean> loadMainPage() throws Exception {
        System.out.println("开始加载主页...");
        Thread.sleep(300);
        System.out.println("加载主页共计耗时 0.3 秒。");
        System.out.println("当前线程名:"+ Thread.currentThread());
        return new AsyncResult<>(true);
    }

    /**
     * 加载个人中心
     *
     * @throws Exception
     */
    @Async("myTaskExecutor")
    public Future<Boolean> loadUserCenter() throws Exception {
        System.out.println("开始加载个人中心...");
        Thread.sleep(500);
        System.out.println("加载个人中心共计耗时 0.5 秒。");
        System.out.println("当前线程名:"+ Thread.currentThread());
        return new AsyncResult<>(true);
    }

    /**
     * 加载活动信息
     *
     * @throws Exception
     */
    @Async("myTaskExecutor")
    public Future<Boolean> loadActivityInfo() throws Exception {
        System.out.println("开始加载活动信息...");
        Thread.sleep(800);
        System.out.println("加载活动信息共计耗时 0.8 秒。");
        System.out.println("当前线程名:"+ Thread.currentThread());
        return new AsyncResult<>(true);
    }
}

单元测试,可以看到线程名的前缀都是我们配置的:

开始加载个人中心...
开始加载主页...
开始加载活动信息...
加载主页共计耗时 0.3 秒。
当前线程名:Thread[myTask-1,5,main]
加载个人中心共计耗时 0.5 秒。
当前线程名:Thread[myTask-2,5,main]
加载活动信息共计耗时 0.8 秒。
当前线程名:Thread[myTask-3,5,main]
加载网站信息完毕,共计耗时:874 毫秒

了解Future:

Future是对于具体的 Runnable 或者 Callable 任务的执行结果进行取消、查询是否完成、获取结果的接口。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

Futrue接口定义如下:

package java.util.concurrent;
public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}

cancel 方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。

isCancelled 方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

isDone 方法表示任务是否已经完成,若任务完成,则返回true;

get() 方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

get(long timeout, TimeUnit unit) 用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

也就是说Future提供了三种功能:判断任务是否完成;能够中断任务;能够获取任务执行结果。

我们测试一下第⑤个方法:

在类 LoadMessageTest 里添加获取用户年龄的方法:

/**
     * 获取用户年龄
     * @return
     * @throws Exception
     */
    @Async("myTaskExecutor")
    public Future<Integer> loadUserAge()throws Exception{
        System.out.println("开始获取用户年龄...");
        Integer age =10;
        Thread.sleep(3000);//休眠3秒
        return new AsyncResult<>(age);
    }

测试类里添加测试方法:

/**
	 * 测试获取用户年龄
	 * @throws Exception
	 */
	@Test
	public void getUserAgeTest()throws Exception{
		Future<Integer> task = loadMessageTest.loadUserAge();//获取用户年龄
		Integer age = task.get(2, TimeUnit.DAYS.SECONDS);//规定2秒内需要返回数据
		System.out.println("用户年龄="+age);
	}

测试结果:超时报错

我们修改测试方法,延长时间:

/**
	 * 测试获取用户年龄
	 * @throws Exception
	 */
	@Test
	public void getUserAgeTest()throws Exception{
		Future<Integer> task = loadMessageTest.loadUserAge();//获取用户年龄
		Integer age = task.get(5, TimeUnit.DAYS.SECONDS);//规定5秒内需要返回数据
		System.out.println("用户年龄="+age);
	}

测试结果:

开始获取用户年龄...
用户年龄=10

O的K,SpringBoot的学习暂时就告一段落。接下来是SpringCloud的学习,微服务架构。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值