springboot-@Async注解和线程池的简单使用

简单使用(不配置线程池)

@Service
public class AsyncService {
	@Async
	public void hello() {
		System.out.println("**start**");
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("线程【" + Thread.currentThread().getName() + "】已结束");
	}
}
@RestController
public class AsyncController {
	@Resource
	AsyncService asyncService;
	@GetMapping("/hello")
	public String hello() {
		System.out.println("当前请求的线程名称为:【" + Thread.currentThread().getName() + "】");
		asyncService.hello();
		return "success";
	}
}

然后再在主入口加注解 @EnableAsync 即可。
现在访问localhost:3000/hello时,页面先返回success,然后再在后台打印其他。
注意:如果某个异步方法(标注了@Async)调用同一个类下的异步方法,那@Async会失效的。

默认情况下@Async注解使用的是SimpleAsyncTaskExecuor,但是这个不是真的线程池,这个类不会重用线程,每次调用都会创建一个新线程。
SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方。
ConcurrentTaskExecutor: Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
ThreadPoolTaskScheduler:可以使用cron表达式。
ThreadPoolTaskExecutor : 最常使用,【推荐】。其实质是对java.util.concurrent.ThreadPoolExecutor的包装。
作者:泓落飞涯
链接:https://www.jianshu.com/p/1bb51607dd9f

配置线程池

Spring中存在接口AsyncConfigurer该接口就是用来配置异步线程池的接口,它有两个方法,getAsyncExecutor和getAsyncUncaughtExceptionHandler,第一个方法是获取一个线程池,第二个方法是用来处理异步线程中发生的异常,需要实现。
前面的代码不改,只是在加一个AsyncConfig类,内容为

@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig implements AsyncConfigurer{
	@Override
	public Executor getAsyncExecutor() {
		ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
		// 核心线程数
		taskExecutor.setCorePoolSize(10);
		// 最大线程数
		taskExecutor.setMaxPoolSize(30);
		// 线程队列最大线程数
		taskExecutor.setQueueCapacity(2000);
		// 初始化线程池
		taskExecutor.initialize();
		return taskExecutor;
	}
	@Override
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return (ex, method, params)->{
			log.error("Error occurs in async method:{}", ex.getMessage());
		};
		// 应返回一个函数式接口,这里使用了lambda表达式简写
	}
}

输出结果:
在这里插入图片描述
还有其他常用参数:

  • keepAliveTime:超过corePoolSize数量的线程最大空闲(等待)时间,默认60s
    也就是说,当poolSize>corePoolSize时,如果一个线程的等待时间超过这个值,就会被销毁。
  • allowCoreThreadTimeOut:销毁机制。如果为true,则线程数量销毁到0个。默认是false。
  • ThreadNamePrefix:线程池前缀名
  • RejectedExecutionHandler:拒绝处理的策略
    在这里插入图片描述
    AbortPolicy:抛出 RejectedExecutionException
    CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程执行
    DiscardOlderstPolicy:抛弃最老的未处理请求,然后重试 execute
    DiscardPolicy:直接抛弃

带返回值的异步方法

一般是将异步方法的返回值使用接口Future、ListenableFuture或类AsyncResult进行包装,也就是将返回值作为泛型传入到上述接口或类中。

Future接口

public interface Future<V> {
	// 取消任务。如果取消成功返回true。mayInterruptIfRunning为true表示可以取消正在执行中的任务。如果任务已完成,
	// 则一律返回false;如果任务还未执行,则一律返回true
	boolean cancel(boolean mayInterruptIfRunning);
	// 表示任务是否被取消成功
	boolean isCancelled();
	// 表示任务是否已完成
	boolean isDone();
	// 获取执行结果。这个方法会产生阻塞,会一直等到任务执行完毕才返回
	V get() throws InterruptedException, ExecutionException;
	// 获取执行结果。如果在指定时间内,还未获取到结果,就直接返回null
	V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

ListenableFuture接口

public interface ListenableFuture<T> extends Future<T> {
	void addCallback(ListenableFutureCallback<? super T> callback);
	void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback);
	default CompletableFuture<T> completable() {
		CompletableFuture<T> completable = new DelegatingCompletableFuture<>(this);
		addCallback(completable::complete, completable::completeExceptionally);
		return completable;
	}
}

ListenableFuture继承了Future接口,还额外添加了三个方法,主要用来添加异步现场的回调,可以用来处理异常和获取异步方法返回值。AsyncResult类实现了ListenableFuture接口。

对于Java1.8,其实更加推荐使用CompletableFuture或者guava的ListenableFuture,功能更多。

我的项目里用了 org.springframework.util.concurrent.ListenableFuture,暂时没用那两个。
service层

	@Async
	public ListenableFuture<String> returnMsg() {
		// https://cloud.tencent.com/developer/article/1609499
		System.out.println(Thread.currentThread().getName());
		String message = "Async Method Result";
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return new AsyncResult<>(message);
	}

controller层

	@GetMapping("/page2")
	public String asyncPage2() {
		System.out.println("当前请求的线程名称为:【" + Thread.currentThread().getName() + "】");
		ListenableFuture<String> result = asyncService.returnMsg();
		result.addCallback(new SuccessCallback<String>() {
			@Override
			public void onSuccess(String result) {
				System.out.println("结果是:" + result);
			}
		}, new FailureCallback() {
			@Override
			public void onFailure(Throwable ex) {
				log.error("发生了异常:" + ex.getMessage());
			}
		});
		return "async";
	}

输出结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值