一. springboot的@Async注解实现异步
要在springboot中使用异步调用方法,只要在被调用的方法上面加上@Async
就可以了
1.准备工作
准备一个springboot工程,在Application类上加上EnableAsync
注解开启异步
/**
* @Author: zgd
* @Date: 18/09/12 10:27
* @Description:
*/
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
这个注解如果不加,@Async注解失效
2.controller
写一个同步接口和异步接口
package com.zgd.demo.thread.asyn.controller;
import com.zgd.demo.thread.asyn.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Instant;
/**
* @Author: zgd
* @Date: 18/09/12 10:26
* @Description: hello
*/
@RestController
@RequestMapping("/hello")
public class HelloController {
@Autowired
private HelloService helloService;
/**
* 异步方法
* @return
*/
@RequestMapping("/asyn")
public String getAsynHello(){
long n = Instant.now().toEpochMilli();
//异步
String s = helloService.asynchSayHello();
long f = Instant.now().toEpochMilli();
return s + " 时间: " + (f-n);
}
/**
* 同步方法
* * @return
*/
@RequestMapping("/sync")
public String getSyncHello(){
long n = Instant.now().toEpochMilli();
//异步
String s = helloService.synchSayHello();
long f = Instant.now().toEpochMilli();
return s + " 时间: " + (f-n);
}
}
3.helloService
写一个HelloService接口,并实现该接口
package com.zgd.demo.thread.asyn.service.impl;
import com.zgd.demo.thread.asyn.service.HelloService;
import com.zgd.demo.thread.asyn.service.SleepService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author: zgd
* @Date: 18/09/12 10:43
* @Description:
*/
@Service
public class HelloServiceImpl implements HelloService {
@Autowired
private SleepService sleepService;
@Override
public String synchSayHello() {
try {
sleepService.syncSleep();
return "hello world,这是同步方法";
} catch (InterruptedException e) {
e.printStackTrace();
return "error";
}
}
@Override
public String asynchSayHello() {
try {
System.out.println("主线程 "+Thread.currentThread().getName());
sleepService.asyncSleep();
return "hello world,这是异步方法";
} catch (InterruptedException e) {
e.printStackTrace();
return "error";
}
}
}
这里为了模拟应用场景,将耗时的方法放在另一个service里面,就叫SleepService,同样的,实现这个接口
package com.zgd.demo.thread.asyn.service.impl;
import com.zgd.demo.thread.asyn.service.SleepService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @Author: zgd
* @Date: 18/09/12 10:46
* @Description:
*/
@Service
public class SleepServiceImpl implements SleepService {
@Override
public void syncSleep() throws InterruptedException {
System.out.println("线程名: " +Thread.currentThread().getName());
System.out.println("开始同步休眠3秒");
Thread.sleep(3000);
System.out.println("同步休眠结束");
}
@Override
@Async
public void asyncSleep() throws InterruptedException {
System.out.println("次线程 "+Thread.currentThread().getName());
System.out.println("开始异步休眠3秒");
Thread.sleep(3000);
System.out.println("异步休眠休眠结束");
}
}
两个方法都是休眠3秒,aysncSleep方法上面有一个@Async
注解
4.测试
-
同步
访问 http://localhost:8080/hello/sync
控制台
要3秒的时间才能收到响应 -
异步
访问 http://localhost:8080/hello/asyn
可见主线程和次线程打印出来的线程名不一样,也就是springboot帮我们开启了一个线程去处理
注意事项
- 必须要加
@EnableAsync
注解 - 不能在同一类下调用@Async注解的方法,比如A类下有a和b方法,b方法有@Async注解,不能直接这样a调用b,要把b放到其他类中
- @Async也可以打在类上,这样类下面的所有方法都是异步的(被其他类调用的时候)
二.开启线程实现异步
如果我们没有使用springboot,使用传统的方法怎么异步调用方法?还是开启一个新的线程
1.controller
增加一个接口
/**
* 同步方法
* * @return
*/
@RequestMapping("/thread/asyn")
public String getThreadSyncHello(){
long n = Instant.now().toEpochMilli();
//异步
String s = helloService.threadAsynchSayHello();
long f = Instant.now().toEpochMilli();
return s + " 时间: " + (f-n);
}
2.service
HelloServiceImpl
@Override
public String threadAsynchSayHello() {
//还是休眠3秒
Runnable runnable = ()-> {
try {
sleepService.syncSleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
System.out.println(Thread.currentThread().getName()+" 开启新线程");
new Thread(runnable).start();
return "hello world,这是开启线程的异步方法";
}
3.测试
我们访问 http://localhost:8080/hello/thread/asyn
同样的两个线程名不一样,开启了一个新线程处理,并且多次测试可以看出,自己直接开启线程比@Async要快几毫秒,不过可以忽略不计了