多线程异步回调技术
一、任务代码
1.1 需要执行的任务类
@Component
public class Task {
public String doTaskOne() throws Exception {
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
System.out.println("任务一耗时:" + (end - start) + "毫秒");
return "任务一的执行结果";
}
public String doTaskTwo() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
System.out.println("任务二耗时:" + (end - start) + "毫秒");
return "任务二的执行结果";
}
public String doTaskThree() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("任务三耗时:" + (end - start) + "毫秒");
return "任务三的执行结果";
}
public String doTaskFour() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(4000);
long end = System.currentTimeMillis();
System.out.println("任务四耗时:" + (end - start) + "毫秒");
return "任务四的执行结果";
}
}
1.2 执行任务的业务类
@Service
public class TaskServiceImpl implements ITaskService {
@Autowired
private Task task;
public Map<String, Object> doTaskMethod() throws Exception {
String taskOneResult = task.doTaskOne();
String taskTwoResult = task.doTaskTwo();
String taskThreeResult = task.doTaskThree();
String taskFourResult = task.doTaskFour();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("taskOneResult", taskOneResult);
resultMap.put("taskTwoResult", taskTwoResult);
resultMap.put("taskThreeResult", taskThreeResult);
resultMap.put("taskFourResult", taskFourResult);
return resultMap;
}
}
1.3 测试入口(基于SpringBoot)
@SpringBootTest(classes = {Application.class})
public class TestThread {
@Autowired
private TaskServiceImpl taskService;
@Test
public void test() Exception {
long start = System.currentTimeMillis();
Map<String, Object> result = taskService.doTaskMethod();
long end = System.currentTimeMillis();
System.out.println("任务总耗时:" + (end - start) + "毫秒");
System.out.println("执行结果为:");
System.out.println(result);
}
}
测试入口的代码五轮使用哪种方式都无需改造。
执行结果如下:

发现执行四个任务需要的时间为10秒钟,因为所有任务都是依次执行。
平常业务开发中可能经常遇见这种情况,导致查询接口执行特别慢。
我们可以使用多线程异步回调的技术来优化代码,在无法优化sql语句时提升查询性能。
二、原生Java方式
2.1 使用jdk8新增的CompletableFuture类来优化上面的代码
@Service
public class TaskServiceImpl {
@Autowired
private Task task;
public Map<String, Object> doTaskMethod() throws Exception {
CompletableFuture<String> completableFutureOne = CompletableFuture.supplyAsync(
() -> {
String taskResult = null;
try {
taskResult = task.doTaskOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
return taskResult;
});
CompletableFuture<String> completableFutureTwo = CompletableFuture.supplyAsync(
() -> {
String taskResult = null;
try {
taskResult = task.doTaskTwo();
} catch (InterruptedException e) {
e.printStackTrace();
}
return taskResult;
});
CompletableFuture<String> completableFutureThree = CompletableFuture.supplyAsync(
() -> {
String taskResult = null;
try {
taskResult = task.doTaskThree();
} catch (InterruptedException e) {
e.printStackTrace();
}
return taskResult;
});
CompletableFuture<String> completableFutureFour = CompletableFuture.supplyAsync(
() -> {
String taskResult = null;
try {
taskResult = task.doTaskFour();
} catch (InterruptedException e) {
e.printStackTrace();
}
return taskResult;
});
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("taskOneResult", CompletableFutureOne.get());
resultMap.put("taskTwoResult", CompletableFutureTwo.get());
resultMap.put("taskThreeResult", CompletableFutureThree.get());
resultMap.put("taskFourResult", CompletableFutureFour.get());
return resultMap;
}
}
api解释:
1) 关于lambda表达式创建多线程的解释
创建一个多线程可以使用如下lambda表达式的方式:
Runnable runnable = () -> System.out.println("任务的执行结果");
这个表达式就相当于创建了一个Runnable接口的实现类。->右侧就是run方法的内容。
2) 关于CompletableFuture的解释
字面意思就是可完成的Future。主要是为了解决jdk8之前的Future类带来的弊端。
3) 关于CompletableFuture.supplyAsync()的解释
supplyAsync方法用于创建一个CompletableFuture对象,它的参数接收一个Supplier函数式接口。
该方法创建的CompletableFuture对象会异步执行当前传入的计算任务。
在调用端,可以通过get或join获取任务执行的结果。
4) 关于CompletableFutureOne.get()的解释
可以获取CompletableFuture对象中异步任务的执行结果。
5) 关于CompletableFutureOne.allOf()的解释
异步执行参数里面的异步任务。
6)join()的作用
它可以让线程阻塞在这,等待allOf中的任务都执行完毕
2.2 执行结果

使用多线程加上CompletableFuture优化后,任务的执行的总时间几乎和任务四的执行时间一致。
大大缩短了任务执行的时间。
同时也能依次获取到异步任务的结果。可以说是非常厉害了。
但是发现上面的代码还是比较的冗余。有没有办法再优化一下。
二、SpringBoot+@Async方式
2.1 开启@Async支持
在SpringBoot启动类上加@EnableAsync注解开启@Async支持
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.2 改造Task类
@Component
public class Task {
@Async
public CompletableFuture doTaskOne() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
System.out.println("任务一耗时:" + (end - start) + "毫秒");
return CompletableFuture.completedFuture("任务一的执行结果");
}
@Async
public CompletableFuture doTaskTwo() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
System.out.println("任务二耗时:" + (end - start) + "毫秒");
return CompletableFuture.completedFuture("任务二的执行结果");
}
@Async
public CompletableFuture doTaskThree() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("任务三耗时:" + (end - start) + "毫秒");
return CompletableFuture.completedFuture("任务三的执行结果");
}
@Async
public CompletableFuture doTaskFour() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(4000);
long end = System.currentTimeMillis();
System.out.println("任务四耗时:" + (end - start) + "毫秒");
return CompletableFuture.completedFuture("任务四的执行结果");
}
}
api解释:
1) 关于@Async
@Async注解能将原来的同步方法变为异步方法。
2) 关于CompletableFuture.completedFuture的解释
completedFuture方法是一个静态方法,作用也是创建一个CompletableFuture对象。
源码如下:
public static <U> CompletableFuture<U> completedFuture(U value) {
return new CompletableFuture<U>((value == null) ? NIL : value);
}
2.3 改造TaskServiceImpl
@Service
public class TaskServiceImpl {
@Autowired
private Task task;
public Map<String, Object> doTaskMethod() throws Exception {
CompletableFuture completableFutureOne = task.doTaskOne();
CompletableFuture completableFutureTwo = task.doTaskTwo();
CompletableFuture completableFutureThree = task.doTaskThree();
CompletableFuture completableFutureFour = task.doTaskFour();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("taskOneResult", completableFutureOne.get());
resultMap.put("taskTwoResult", completableFutureTwo.get());
resultMap.put("taskThreeResult", completableFutureThree.get());
resultMap.put("taskFourResult", completableFutureFour.get());
return resultMap;
}
}
此时doTaskMethod中就已经清爽很多了。
2.4 执行结果

发现依旧保持了异步任务的高性能,又让代码更加简洁。还能获取到异步任务的结果。
但是大量使用这种异步任务,对服务器的要求是比较高的。
所以开发中并不需要把所有任务都写成这个样子。
只需要对和上面这些情况类似的情况,就可以使用异步回调的方式来优化。
|