情况:后端涉及到异步操作,那么可能数据还没更新完就直接向前端返回success的结果。这就导致数据不一致。因此需要采用锁或者其他机制,对异步场景进行处理,确保数据的一致性。
下面是一些常用的方法,简单情况直接用CountDownLatch计数器锁即可实现,下面还有多种方法,包括回调,消息队列等。
下面有代码的示例,大家可以根据需要,自行查看!
1. CountDownLatch(计数器锁)
适用于简单的多线程同步场景,等待一个或多个线程完成操作后再继续执行。
java
@PostMapping("syncData")
public AjaxJson syncData() {
CountDownLatch latch = new CountDownLatch(1); // 初始化计数器为1
// 异步操作
asyncService.processData(latch);
try {
// 等待最多5秒
if (!latch.await(5, TimeUnit.SECONDS)) {
return AjaxJson.getError("操作超时");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return AjaxJson.getError("操作被中断");
}
return AjaxJson.getSuccess();
}
// 异步服务中
public void processData(CountDownLatch latch) {
try {
// 执行异步操作...
} finally {
latch.countDown(); // 操作完成,计数器减1
}
}
2. CompletableFuture(Java 8+)
更现代的异步编程方式,支持链式调用和组合多个异步操作。
java
@PostMapping("syncData")
public CompletableFuture<AjaxJson> syncData() {
return CompletableFuture.supplyAsync(() -> {
// 异步操作
return processData();
}).thenApply(result -> {
return AjaxJson.getSuccessData(result);
}).exceptionally(ex -> {
return AjaxJson.getError("操作失败: " + ex.getMessage());
}).completeOnTimeout(
AjaxJson.getError("操作超时"),
5, TimeUnit.SECONDS
);
}
private SomeResult processData() {
// 执行耗时操作...
return someResult;
}
3. 回调机制
适用于事件驱动的架构,通过回调函数处理异步结果。
java
@PostMapping("syncData")
public AjaxJson syncData() {
CompletableFuture<Boolean> future = new CompletableFuture<>();
asyncService.processData(new AsyncCallback() {
@Override
public void onSuccess() {
future.complete(true);
}
@Override
public void onFailure(Exception e) {
future.completeExceptionally(e);
}
});
try {
Boolean result = future.get(5, TimeUnit.SECONDS);
return result ? AjaxJson.getSuccess() : AjaxJson.getError();
} catch (Exception e) {
return AjaxJson.getError(e.getMessage());
}
}
4. 消息队列+数据库状态
适用于分布式系统,通过消息队列和数据库状态保证最终一致性。
java
@PostMapping("startSync")
public AjaxJson startSync() {
String taskId = UUID.randomUUID().toString();
// 初始化任务状态为"处理中"
taskService.createTask(taskId, "PROCESSING");
// 发送消息到队列
messageQueue.send(new SyncMessage(taskId));
return AjaxJson.getSuccessData(taskId);
}
@GetMapping("checkStatus/{taskId}")
public AjaxJson checkStatus(@PathVariable String taskId) {
Task task = taskService.getTask(taskId);
if ("COMPLETED".equals(task.getStatus())) {
return AjaxJson.getSuccess();
} else if ("FAILED".equals(task.getStatus())) {
return AjaxJson.getError(task.getErrorMessage());
}
return AjaxJson.getError("处理中,请稍后再试");
}
// 消费者端
@RabbitListener(queues = "syncQueue")
public void processSyncMessage(SyncMessage message) {
try {
// 处理业务逻辑...
taskService.updateTask(message.getTaskId(), "COMPLETED", null);
} catch (Exception e) {
taskService.updateTask(message.getTaskId(), "FAILED", e.getMessage());
}
}
5. 响应式编程(WebFlux)
使用Spring WebFlux进行非阻塞式编程。
java
@PostMapping("/syncData")
public Mono<AjaxJson> syncData() {
return Mono.fromCallable(() -> {
// 模拟耗时操作
Thread.sleep(2000);
return "处理结果";
})
.timeout(Duration.ofSeconds(5))
.map(result -> AjaxJson.getSuccessData(result))
.onErrorResume(ex -> Mono.just(AjaxJson.getError(ex.getMessage())));
}
最佳实践建议
-
简单场景:使用CountDownLatch或CompletableFuture
-
复杂异步流程:使用CompletableFuture的组合操作
-
分布式系统:使用消息队列+数据库状态
-
高并发系统:考虑响应式编程(WebFlux)
-
超时处理:所有方案都应设置合理的超时时间
-
错误处理:确保异常情况能被妥善处理并反馈给客户端