我们在使用多线程的时候,往往需要创建Thread类,或者实现Runnable接口,如果要使用到线程池,我们还需要来创建Executors,在使用spring中,已经给我们做了很好的支持。只要要@EnableAsync就可以使用多线程。使用@Async就可以定义一个线程任务。通过spring给我们提供的ThreadPoolTaskExecutor就可以使用线程池。
默认情况下,Spring将搜索相关的线程池定义:要么在上下文中搜索唯一的TaskExecutor bean,要么搜索名为“taskExecutor”的Executor bean。如果两者都无法解析,则将使用SimpleAsyncTaskExecutor来处理异步方法调用。
基于@Async标注的方法称为异步方法,方法在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。使用时在SpringBoot主配置类中开启异步即可。
@Async注解来声明一个或多个异步任务,可以加在方法或者类上,加在类上表示这整个类都是使用这个自定义线程池进行操作
如下方式会使@Async失效
一、异步方法使用static修饰
二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
四、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
六、在Async 方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效。
七、调用被@Async标记的方法的调用者不能和被调用的方法在同一类中,因为@Async注解是通过aop代理实现的,视为内部调用!!!!!!!如果非要在一个bean中,使用代理获得当前class的bean后,使用bean调用被标识的方法。
八、使用@Async时要求是不能有返回值的不然会报错的 因为异步要求是不关心结果的
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class AsyncDemoApplication {
/**
* 异步方法
*
* @throws InterruptedException
*/
@Async
public void asyncMethod() throws InterruptedException {
for (int i = 1; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "********" + i);
}
}
public static void main(String[] args) {
try {
//获取上下文
ConfigurableApplicationContext context = SpringApplication.run(AsyncDemoApplication.class, args);
//获取对象bean,进而调用异步方法
context.getBean(AsyncDemoApplication.class).asyncMethod();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "********" + i);
if (2 == i)
Thread.sleep(1000l);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@EnableAsync
@SpringBootApplication
public class SpringBootAsyncTestApplication {
}
无返回值异步方法
@Async
public void asyncMethodWithNoReturnType() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("asyncMethodWithNoReturnType...");
}
含返回值异步方法
@Async
public Future<String> asyncMethodWithReturnType() {
try {
Thread.sleep(3000);
return new AsyncResult<String>("success");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("asyncMethodWithReturnType...");
return null;
}
Future是对于具体的 Runnable 或者 Callable 任务的执行结果进行取消、查询是否完成、获取结果的接口,必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果,包含了以下几个方法。
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;
}
1.cancel方法的作用是取消任务,取消任务成功则返回true,反之返回false。参数 mayInterruptIfRunning 表示是否允许取消正在执行却没有执行完毕的任务。
运行cancel方法取消任务时:
i.若任务已完成:则无论 mayInterruptIfRunning 为 true 或 false,此方法都返回 false,即取消已经完成的任务都会返回false。
i.若任务正在执行:
mayInterruptIfRunning 设置为 true,则返回true。
mayInterruptIfRunning 设置为false,则返回false。
iii.如果任务未执行,则无论mayInterruptIfRunning为true还是false,都返回true。
2.isCancelled方法的作用是判断任务是否被取消成功,若在任务正常完成前被取消,则返回 true。
3.isDone方法的作用是判断任务是否已经完成,若任务已完成,则返回true。
4.get()方法的作用是获取执行结果,注意此方法会产生阻塞,等到任务执行完毕后才能获得执行结果。
5.get(long timeout, TimeUnit unit)方法的作用同样是获取执行结果,若在指定时间内还未获取到执行结果,则返回null。
Future提供了三种功能:
- 判断任务是否完成
- 能够中断任务
- 能够获取任务执行结果
@RestController
public class AsyncController {
@Autowired
AsyncService asynSerivce;
@GetMapping("/callWithNoReturnType")
public String callWithNoReturnType() {
asynSerivce.asyncMethodWithNoReturnType();
return "success";
}
@GetMapping("/callWithReturnType")
public String callWithReturnType() {
Future<String> future=asynSerivce.asyncMethodWithReturnType();
try {
return future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return "fail";
}
调用无返回值的异步方法asyncWithNoReturnType时,会立即返回返回值。但调用含返回值异步方法asyncWithReturnType时,由于我们调用了get()方法,会在等待3000毫秒后,才返回返回值。