Java 8响应式异步即搜即输实现的竞态排查与代码简化咨询
现有实现的问题
- 语法错误:你在
synchronized代码块内定义的patternCopy和modCount属于块局部变量,块外无法访问,代码无法正常编译。 - 冗余同步:你用到的
AtomicReference和AtomicLong本身就是线程安全的原子类,对它们的单个读写操作自带原子性,不需要额外加synchronized块同步,多余的同步会徒增性能开销。 - 严重竞态条件:当
busy.tryLock()失败直接返回时,可能出现刚好正在运行的搜索任务已经完成所有modCount校验,即将返回成功结果,此时你刚更新的pattern和modCount不会触发新的搜索任务,运行中的任务结束后释放锁,没有新的任务执行最新的搜索,会直接丢失用户的最新输入。 - 搜索无法真正中止:当前
doSearch仅在数据库查询完成、结果处理阶段才校验modCount,如果数据库查询本身耗时很长,就算中间输入已经变更,也必须等查询结束才能放弃本次结果,这段时间的CPU、IO资源会被浪费。 - 异常处理缺失:如果
doSearch过程中抛出未捕获的异常,虽然锁会被释放,但没有对应的错误处理逻辑,可能导致搜索功能直接静默失效。
Java 8简化实现方案
Java 8提供的CompletableFuture完全可以替代你自己维护的锁、版本计数逻辑,代码量可以大幅减少:
- 不需要自己维护任务调度状态,每次输入变更时保留最新的
CompletableFuture引用,新输入触发时直接取消之前未完成的旧任务即可,不会出现丢输入的问题。 - 可以配合
ScheduledExecutorService实现输入防抖,比如用户连续输入时延迟200~300ms再执行搜索,避免频繁触发数据库请求。
示例简化代码:
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private CompletableFuture<List<ResultType>> latestSearch; private Consumer<List<ResultType>> resultConsumer; // 每次用户输入时调用 void onInputChanged(String pattern) { // 取消之前未完成的搜索任务,mayInterruptIfRunning设为true可以尝试打断正在运行的任务 if (latestSearch != null && !latestSearch.isDone()) { latestSearch.cancel(true); } // 可选:加300ms防抖,避免输入过程中频繁触发搜索 latestSearch = CompletableFuture.supplyAsync(() -> doSearch(pattern), scheduler) .thenAccept(result -> resultConsumer.accept(result)) .exceptionally(e -> { // 处理搜索异常,取消任务抛出的CancellationException可以直接忽略 if (!(e instanceof CancellationException)) { // 其他业务异常按需处理 e.printStackTrace(); } return null; }); } List<ResultType> doSearch(String pattern) { // 直接执行数据库查询、结果处理即可,任务被取消时会自动抛出中断异常终止执行 List<ResultType> results = jdbcTemplate.query("select * from xxx where content like ?", ResultType.class, "%" + pattern + "%"); return results; }
内容的提问来源于stack exchange,提问作者Aaron Digulla
相关产品推荐
相关产品推荐

