异步调用, 光用@Async也没用, 除非不关系结果 🏙️
真正的爱情是不能用言语表达的,行为才是忠心的最好说明
然而,Java 的 Future
接口并不提供 addListener
这个方法。为了解决这个问题,你可以使用第三方库,如 Google 的 Guava 库,它提供了一个 ListenableFuture
接口,可以让你添加一个回调函数,在任务完成时执行。
以下是一个使用 Guava 的 ListenableFuture
的示例:
首先,将 Guava 库添加到你的项目中。如果你使用的是 Maven,可以在 pom.xml
中添加以下依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
然后,你可以使用 ListenableFuture
和 Futures.addCallback()
方法添加监听器:
import com.google.common.util.concurrent.*;
import java.util.concurrent.Executors;
public class ListenableFutureExample {
public static void main(String[] args) {
// 创建一个 ListeningExecutorService
ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
// 提交一个异步任务并获得一个 ListenableFuture 对象
ListenableFuture<Integer> future = executor.submit(() -> {
System.out.println("Async task is running...");
Thread.sleep(1000);
System.out.println("Async task is completed.");
return 42;
});
// 为 ListenableFuture 添加一个回调函数
Futures.addCallback(future, new FutureCallback<Integer>() {
@Override
public void onSuccess(Integer result) {
System.out.println("Result: " + result);
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
}, executor);
// 关闭线程池
executor.shutdown();
}
}
在这个示例中,我们使用 MoreExecutors.listeningDecorator()
将普通的 ExecutorService
装饰为 ListeningExecutorService
。然后,我们提交一个异步任务并获得一个 ListenableFuture
对象。接下来,我们使用 Futures.addCallback()
方法为 ListenableFuture
添加一个回调函数。这个回调函数包含 onSuccess()
和 onFailure()
方法,分别在任务成功完成和发生异常时执行。最后,我们关闭线程池。
运行上述代码,你将看到以下输出:
Async task is running...
Async task is completed.
Result: 42
在这个示例中,我们没有显式地调用 future.get()
方法来获取计算结果,而是通过在 ListenableFuture
上添加回调函数来实现。这样,我们可以在任务完成时执行相应的操作,而不需要阻塞主线程。
使用 ListenableFuture
可以使你的代码更具可读性,因为你可以在同一个地方定义异步任务和完成时的操作。此外,通过添加回调函数,你可以避免阻塞主线程,从而提高应用程序的响应性能。
总之,Future
和 ListenableFuture
提供了一种处理异步任务的灵活方法。你可以使用它们来获取异步计算结果,或者添加回调函数在任务完成时执行。这有助于提高应用程序的可读性和响应性能。
springboot 实现类似 ListenableFuture 的回调功能
在 Spring Boot 中,可以使用 CompletableFuture
类来实现类似 Guava 库的 ListenableFuture
的回调功能,而不会导致阻塞。CompletableFuture
是 Java 8 中引入的一个功能强大的异步编程工具,它实现了 Future
接口,并提供了一系列非阻塞的回调方法。
下面是一个简单的示例,展示如何在 Spring Boot 项目中使用 CompletableFuture
实现异步回调:
- 首先,创建一个异步任务,该任务返回一个
CompletableFuture
对象:
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
@Async
public CompletableFuture<String> asyncTask() {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture("异步任务完成");
}
}
- 在使用异步任务的地方,添加回调方法:
import java.util.concurrent.CompletableFuture;
@Service
public class MyService {
@Autowired
private AsyncService asyncService;
public void executeTask() {
CompletableFuture<String> future = asyncService.asyncTask();
// 添加回调方法,处理正常完成的情况
future.thenAccept(result -> {
System.out.println("任务完成,结果:" + result);
});
// 添加回调方法,处理异常情况
future.exceptionally(ex -> {
System.out.println("任务异常:" + ex.getMessage());
return null;
});
// 非阻塞执行
System.out.println("主线程继续执行");
}
}
asyncService.asyncTask() 会立即返回一个 CompletableFuture 对象,而不会等待异步任务执行完毕。这是因为 CompletableFuture 本身是一个表示异步计算的占位符,它允许您在稍后的时间点处理异步任务的结果。
当您调用 asyncService.asyncTask() 时,它会在后台启动一个异步任务,并立即返回一个 CompletableFuture 对象。您可以在这个对象上添加回调函数,以便在异步任务完成时执行特定的操作。这些回调函数是非阻塞的,也就是说,它们不会影响主线程的执行。
在上面的例子中,CompletableFuture future = asyncService.asyncTask() 这行代码会立即返回一个 CompletableFuture 对象,然后您可以添加回调方法,如 future.thenAccept() 和 future.exceptionally()。这些回调方法会在异步任务完成或发生异常时被调用,而主线程可以继续执行其他任务。
- 最后,确保 Spring Boot 项目已正确配置异步支持:
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync
public class AsyncConfiguration {
}
这样,在执行 MyService.executeTask()
方法时,异步任务将在后台运行,并在完成时执行回调方法,主线程可以继续执行其他任务,而不会被阻塞。
感觉有点类似 JavaScript 中 Promise 的用法
Promise
Promise 对象在 JavaScript 中更类似于 Java 中的 CompletableFuture,因为它们都提供了一种处理异步操作的方式,可以对异步操作的完成和失败状态进行处理。
在 JavaScript 中,Promise 对象可以通过构造函数来创建,构造函数需要传递一个执行器函数(executor function),它有两个参数:resolve 和 reject。resolve 函数用于将 Promise 对象的状态设置为成功(fulfilled),并传递一个结果值;reject 函数用于将 Promise 对象的状态设置为失败(rejected),并传递一个错误原因。
例如,以下代码展示了如何创建一个 Promise 对象:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
resolve('success');
} else {
reject('failure');
}
}, 1000);
});
这个 Promise 对象会等待 1 秒钟,然后随机决定是成功还是失败,并将相应的状态和值传递给 resolve 和 reject 函数。
要处理 Promise 对象的完成和失败状态,可以使用 then() 方法和 catch() 方法。then() 方法用于注册成功状态的回调函数,catch() 方法用于注册失败状态的回调函数。这些回调函数将在 Promise 对象的状态发生相应变化时被调用,并且可以访问到相应的结果值或错误原因。
例如,以下代码展示了如何使用 then() 方法和 catch() 方法来处理 Promise 对象的状态:
myPromise.then((result) => {
console.log('Promise resolved with result:', result);
}).catch((error) => {
console.error('Promise rejected with error:', error);
});
这个代码会注册一个成功状态的回调函数,用于输出成功时的结果值,以及一个失败状态的回调函数,用于输出失败时的错误原因。如果 Promise 对象被解决,则会调用成功回调函数;如果 Promise 对象被拒绝,则会调用失败回调函数。
可以链式调用 then() 方法来处理多个 Promise 对象的状态。例如,以下代码展示了如何使用 Promise 对象的 then() 方法来进行链式调用:
可以链式调用 then() 方法来处理多个 Promise 对象的状态。例如,以下代码展示了如何使用 Promise 对象的 then() 方法来进行链式调用:
promise1().then((result1) => {
console.log('promise1 resolved with result:', result1);
return promise2();
}).then((result2) => {
console.log('promise2 resolved with result:', result2);
return promise3();
}).then((result3) => {
console.log('promise3 resolved with result:', result3);
}).catch((error) => {
console.error('Promise rejected with error:', error);
});
这个代码中,先调用 promise1() 方法,等待它的解决,然后将结果传递给第一个 then() 方法。第一个 then() 方法会调用 promise2() 方法,等待它的解决,并将结果传递给第二个 then() 方法。第二个 then() 方法会调用 promise3() 方法,等待它的解决,并将结果传递给第三个 then() 方法。如果任何一个 Promise 对象被拒绝,都会调用 catch() 方法。
综上所述,Promise 对象提供了一种简单而强大的方式来处理异步操作的状态,可以方便地处理成功和失败的情况,以及链式调用多个 Promise 对象。在 JavaScript 中,Promise 对象是非常常用的异步编程工具,也是一种比较常见的处理异步操作的方式。