有以下两个实现功能的方法
public String getName(){
// 模拟功能的耗时操作
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return "csdn:sorako";
}
public String getCover(){
// 模拟功能的耗时操作
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return "cover";
}
有一个需求,需要异步调用获得结果之后,在执行数据库Mapper插入数据库操作(用打印表示)
一、异步调用一个方法
如果是如下写法:
FileInfo fileInfo = new FileInfo();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> getName());
future.thenAccept(name -> fileInfo.setFileName(name)).exceptionally(ex -> {
log.error("异步执行失败");
return null;
});
//执行Mapper。。。。。。。。。。
System.out.println("fileInfo = " + fileInfo);
答案是mapper执行sql后插入数据库的数据为空
在异步调用时,会直接执行主线程的后续部分,没有等异步执行返回结果就直接执行mapper中的操作了。
所以,可以把mapper操作放到回调函数中执行
FileInfo fileInfo = new FileInfo();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> getName());
future.thenAccept(name -> {
fileInfo.setFileName(name);
//执行Mapper。。。。。。。。。。
System.out.println("fileInfo = " + fileInfo);
}).exceptionally(ex -> {
log.error("异步执行失败");
return null;
});
但是执行,会发现控制条没有任何信息????????
因为主线程已经结束了,异步调用还没走到mapper操作打印那里,但是如果在主线程还没结束,是会打印出来信息的。我们让主线程暂停几秒模拟一些后续操作。
结果如下
但是事实上我们无法预测是否主线程后续操作的结束时间前,异步调用能执行
所以我们可以让主线程必须在异步调用返回结果后在执行
FileInfo fileInfo = new FileInfo();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> getName());
future.thenAccept(name -> {
fileInfo.setFileName(name);
//执行Mapper。。。。。。。。。。
System.out.println("fileInfo = " + fileInfo);
}).exceptionally(ex -> {
log.error("异步执行失败");
return null;
});
// 等待异步任务完成
try {
future.get(); // 或者 future.join();
} catch (Exception e) {
log.error("等待异步任务时发生错误: " + e.getMessage());
}
也可以简化:
在这里.get()会等待异步调用完成,但是也会可能抛出异常,需要抛出捕获,我们可以用.join()代替
这两个的区别(AI生成):
===================================================================
`future.get()` 和 `future.join()` 都是用于处理并发编程中异步任务的结果,但它们属于不同的编程框架或库,并且具有不同的用途和行为。
### `future.get()`
- **库**: Java 的 `java.util.concurrent.Future` 接口。
- **用途**: 用于获取异步计算的结果。
- **行为**:
- 调用 `future.get()` 会阻塞调用线程,直到异步任务完成并返回结果。
- 如果任务尚未完成,调用线程会被挂起,直到任务完成。
- 如果任务执行过程中抛出异常,`future.get()` 会将异常重新抛出。
- **示例**:
Future future = executorService.submit(() -> {
// 模拟一个异步任务
Thread.sleep(2000);
return 123;
});
try {
Integer result = future.get(); // 这会阻塞直到任务完成
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
```
### `future.join()`
- **库**: Java 的 `Thread` 类或 Kotlin 的协程(`join()` 在 Kotlin 协程中表现为 `await()`)。
- **用途**: 用于等待一个线程或协程完成。
- **行为**:
- 在 Java 中,`thread.join()` 会使当前线程(调用 `join()` 的线程)等待,直到目标线程终止。
- **示例**:
Thread thread = new Thread(() -> {
// 模拟一个任务
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“Thread finished”);
});
thread.start();
try {
thread.join(); // 这会阻塞直到线程完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“Main thread continues”);
```
### 总结
- `future.get()` 是 Java 并发库中的方法,用于获取异步任务的结果,并阻塞调用线程直到任务完成。
- `join()` 是 Java 线程的方法,用于等待一个线程完成。
它们虽然都涉及到等待某个任务完成,但应用场景和所属的库不同。
=====================================================================
但是有人就会问了,在这里等待异步调用完成再执行后续操作,那不如不使用异步操作???好像确实是这样
那假如是执行两个异步操作呢?
二、异步调用两个方法
异步调用执行两个方法
FileInfo fileInfo = new FileInfo();
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> getName());
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> getCover());
// 使用allOf等待所有Future完成
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);
// 注册回调,当所有Future都完成时执行
allFutures.thenRun(() -> {
try {
String fileName = future1.get();
String fileCover = future2.get();
fileInfo.setFileName(fileName);
fileInfo.setFileCover(fileCover);
//执行Mapper。。。。。。。。。。
System.out.println("fileInfo = " + fileInfo);
} catch (Exception e) {
log.error("异步执行失败");
}
}).exceptionally(ex -> {
ex.printStackTrace();
return null;
}).join();
当然,还是用使用.join()或者.get()保证异步调用执行完,这里我们可以看出执行时间2s.24ms
那如果不使用异步调用呢?执行时间4s.35ms
可以看出在多个异步调用执行复杂操作时,效率是更高的。
当然,这里的前提都是,主程序的后续执行需要异步调用后返回执行结果,如果不需要异步调用的返回结果。那首选异步操作。