springboot整合@Async
@Async注解作用:实现异步方法调用
应用场景列子:多线程计费问题
同步:多个线程访问同一个资源 A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去
异步:多个线程访问同一个资源 A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程仍然请求的到,A线程无需等待
启动类
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* @program:
* @description:
* @author:
* @create:
**/
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Controller层
package com.example.demo.controller;
import com.example.demo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.Future;
/**
* @program:
* @description:
* @author:
* @create:
**/
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@RequestMapping(value = "/test1",method = RequestMethod.GET)
public String asyncTask1() throws InterruptedException {
long start = System.currentTimeMillis();
asyncService.doTaskOne1();
asyncService.doTaskTwo1();
asyncService.doTaskThree1();
return String.format("任务执行成功,耗时{%s}", System.currentTimeMillis() - start);
}
@RequestMapping(value = "/test2",method = RequestMethod.GET)
public String asyncTask2() throws InterruptedException {
long start = System.currentTimeMillis();
Future<String> task1 = asyncService.doTaskOne2();
Future<String> task2 = asyncService.doTaskTwo2();
Future<String> task3 = asyncService.doTaskThree2();
// 三个任务都调用完成,退出循环等待
while (!task1.isDone() || !task2.isDone() || !task3.isDone()) {
Thread.sleep(1000);
}
long end = System.currentTimeMillis();
System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
return String.format("任务执行成功,耗时{%s}", System.currentTimeMillis() - start);
}
}
Service层
package com.example.demo.service;
import org.springframework.scheduling.annotation.Async;
import java.util.concurrent.Future;
/**
* @program: demo
* @description:
* @author:
* @create:
**/
public interface AsyncService {
@Async
void doTaskOne1() throws InterruptedException;
@Async
void doTaskTwo1() throws InterruptedException;
@Async
void doTaskThree1() throws InterruptedException;
@Async
Future<String> doTaskOne2() throws InterruptedException;
@Async
Future<String> doTaskTwo2() throws InterruptedException;
@Async
Future<String> doTaskThree2() throws InterruptedException;
}
Impl层
package com.example.demo.impl;
import com.example.demo.service.AsyncService;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.Random;
import java.util.concurrent.Future;
/**
* @program:
* @description:
* @author:
* @create:
**/
@Service
public class AsyncServiceImpl implements AsyncService {
private static Random random = new Random();
@Override
public void doTaskOne1() throws InterruptedException {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
}
@Override
public void doTaskTwo1() throws InterruptedException {
System.out.println("开始做任务二");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
}
@Override
public void doTaskThree1() throws InterruptedException {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
}
@Override
public Future<String> doTaskOne2() throws InterruptedException {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务一完成");
}
@Override
public Future<String> doTaskTwo2() throws InterruptedException {
System.out.println("开始做任务二");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务二完成");
}
@Override
public Future<String> doTaskThree2() throws InterruptedException {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务三完成");
}
}
test1测试结果
test2测试结果
@Async用于多线程的 可以加上注解多线程
在service层加上value例如:@Async(value = “asyncServiceExecutor”)
修改后
package com.example.demo.service;
import org.springframework.scheduling.annotation.Async;
import java.util.concurrent.Future;
/**
* @program: demo
* @description:
* @author:
* @create:
**/
public interface AsyncService {
@Async(value = "asyncServiceExecutor")
void doTaskOne1() throws InterruptedException;
@Async(value = "asyncServiceExecutor")
void doTaskTwo1() throws InterruptedException;
@Async(value = "asyncServiceExecutor")
void doTaskThree1() throws InterruptedException;
@Async(value = "asyncServiceExecutor")
Future<String> doTaskOne2() throws InterruptedException;
@Async(value = "asyncServiceExecutor")
Future<String> doTaskTwo2() throws InterruptedException;
@Async(value = "asyncServiceExecutor")
Future<String> doTaskThree2() throws InterruptedException;
}
线程池方法
package com.cudatec.infra.acm.config;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@EnableAsync
@Configuration
public class ExecutorConfig {
private static final Logger logger = (Logger) LoggerFactory.getLogger(ExecutorConfig.class);
@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;
@Value("${async.executor.thread.await.termination.seconds}")
private String awaitTerminationSeconds;
@Value("${async.executor.thread.alive.seconds}")
private int aliveSeconds;
@Bean(name = "asyncServiceExecutor")
public Executor asyncServiceExecutor() {
logger.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(namePrefix);
//配置线程池中的线程的名称前缀
executor.setKeepAliveSeconds(aliveSeconds);
//用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁
executor.setWaitForTasksToCompleteOnShutdown(true);
//该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
executor.setAwaitTerminationSeconds(60);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
package com.cudatec.infra.acm.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);
private void showThreadPoolInfo(String prefix) {
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if (null == threadPoolExecutor) {
return;
}
logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2. do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}
线程配置
application.properties
# 异步线程配置
# 配置核心线程数
async.executor.thread.core_pool_size = 50
# 配置最大线程数
async.executor.thread.max_pool_size = 100
# 配置队列大小
async.executor.thread.queue_capacity = 99999
# 配置线程池中的线程的名称前缀
async.executor.thread.name.prefix = async-service-
# 配置线程池中的线程等待时间后自动关闭
async.executor.thread.await.termination.seconds = 60
async.executor.thread.alive.seconds = 60
异步缺点
A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程仍然请求的到,A线程无需等待的问题(例子:一个客户发起一次扣费,由于A B线程都请求到 出现多笔扣费)
解决方法:锁(避免重复记录)
Impl层 方法上加上注解
@DistributedLock(param = “锁对象参数,不设置位置默认第一个”, fairLock = true, tryLock = true)
//锁的是用户User的第一个参数userId
@Override
@DistributedLock(param = "userId", fairLock = true, tryLock = true)
public void doTaskOne1(User user) throws InterruptedException {
}
package com.example.demo.common.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* @program:
* @description:
* @author:
* @create:
**/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
/**
* 锁的名称。
* 如果lockName可以确定,直接设置该属性。
*/
String lockName() default "";
/**
* lockName后缀
*/
String lockNamePre() default "";
/**
* lockName后缀
*/
String lockNamePost() default "lock";
/**
* 获得锁名时拼接前后缀用到的分隔符
* @return
*/
String separator() default ".";
/**
* <pre>
* 获取注解的方法参数列表的某个参数对象的某个属性值来作为lockName。因为有时候lockName是不固定的。
* 当param不为空时,可以通过argNum参数来设置具体是参数列表的第几个参数,不设置则默认取第一个。
* </pre>
*/
String param() default "";
/**
* 将方法第argNum个参数作为锁
*/
int argNum() default 0;
/**
* 是否使用公平锁。
* 公平锁即先来先得。
*/
boolean fairLock() default false;
/**
* 是否使用尝试锁。
*/
boolean tryLock() default false;
/**
* 最长等待时间。
* 该字段只有当tryLock()返回true才有效。
*/
long waitTime() default 30L;
/**
* 锁超时时间。
* 超时时间过后,锁自动释放。
* 建议:
* 尽量缩简需要加锁的逻辑。
*/
long leaseTime() default 5L;
/**
* 时间单位。默认为秒。
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}