情况:后端涉及到异步操作,数据还没更新完就直接向前端返回success的结果。

情况:后端涉及到异步操作,那么可能数据还没更新完就直接向前端返回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())));
}

最佳实践建议

  1. 简单场景:使用CountDownLatch或CompletableFuture

  2. 复杂异步流程:使用CompletableFuture的组合操作

  3. 分布式系统:使用消息队列+数据库状态

  4. 高并发系统:考虑响应式编程(WebFlux)

  5. 超时处理:所有方案都应设置合理的超时时间

  6. 错误处理:确保异常情况能被妥善处理并反馈给客户端

相关推荐
观北海2 分钟前
网络安全蓝队常用工具全景与实战指南
安全·web安全
rannn_1117 分钟前
【Javaweb学习|黑马笔记|Day1】初识,入门网页,HTML-CSS|常见的标签和样式|标题排版和样式、正文排版和样式
css·后端·学习·html·javaweb
柏油15 分钟前
Spring @Cacheable 解读
redis·后端·spring
lingggggaaaa1 小时前
小迪安全v2023学习笔记(六十一讲)—— 持续更新中
笔记·学习·安全·web安全·网络安全·反序列化
柏油1 小时前
Spring @TransactionalEventListener 解读
spring boot·后端·spring
不太可爱的叶某人2 小时前
【学习笔记】Java并发编程的艺术——第6章 Java并发容器和框架
java·笔记·学习
两码事3 小时前
告别繁琐的飞书表格API调用,让飞书表格操作像操作Java对象一样简单!
java·后端
shark_chili3 小时前
面试官再问synchronized底层原理,这样回答让他眼前一亮!
后端
灵魂猎手3 小时前
2. MyBatis 参数处理机制:从 execute 方法到参数流转全解析
java·后端·源码
紫金桥软件3 小时前
紫金桥RealSCADA:国产工业大脑,智造安全基石
安全·系统安全·软件工程