news 2026/10/21 23:10:18

流式输出的异步并发问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
流式输出的异步并发问题

回调桥接

Future解决的问题

  • 可以异步等待
  • Future是一个占位符,代表"未来某个时刻才有的结果"
// 同步 —— 卡在这行,等结果回来才往下走Stringresult=blockingCall();// 异步 —— 立刻返回一个"凭证"(Future),实际结果稍后才就绪Future<String>ticket=asyncCall();// ... 你可以先做其他事 ...Stringresult=ticket.get();// 这里才真正等

Future的问题

Future<String>f=executor.submit(()->"hello");// 硬伤 1:只能阻塞等,没法说"好了之后自动帮我做下一步"f.get();// 阻塞当前线程// 硬伤 2:没法手动把一个值放进去(只能靠线程执行完自动填)
  • executor.submit是指把括号里里面的任务提交给线程池子,线程池安排一个线程异步执行
  • 一个问题是当我get()结果的时候,如果没有执行完的话会阻塞在这个地方
  • 问题二是指有办法主动往 Future 里塞一个结果,结果只能等线程池里的任务跑完自动填入
  • 比如这种情况就要主动填入
// 假设你已经知道结果,想直接包成 Future 返回publicFuture<String>getData(){if(cacheHit){// 想直接返回结果,不走线程池// Future 做不到!没有办法手动填值}returnexecutor.submit(()->fetchFromDB());}

CompletableFuture解决了手动填值和链式编排

  • 手动填值
// 你可以在任何时刻、任何地方,把结果"塞"进去f.complete("结果来了");// 或者塞一个异常f.completeExceptionally(newRuntimeException("出错了"));
  • 链式编排
CompletableFuture<String>f=CompletableFuture.supplyAsync(()->"hello");// 不阻塞,而是注册"好了之后做什么"f.thenApply(s->s+" world")// hello → hello world.thenApply(s->s.toUpperCase())// → HELLO WORLD.thenAccept(result->System.out.println(result));// 自动触发,不用手动 get

langchian4j里面为什么要Future

  • 需要Future的场景是:你要等回调结果,然后用这个结果做下一步
  • 因为流式输出是回调实现的
  • 回调是一次性的、嵌套的、难以组合的,而 Future 是可等待的、可链式的、可组合的。翻译一次,后续编排就从"痛苦"变成"自然"

给我印象最深刻的例子:先生成大纲然后写正文

  • 纯回调
model.chat(大纲请求,newStreamingChatResponseHandler(){@OverridepublicvoidonCompleteResponse(ChatResponseoutline){// 第一步完成了,现在发起第二步model.chat(正文请求(outline),newStreamingChatResponseHandler(){@OverridepublicvoidonCompleteResponse(ChatResponsebody){// 第二步完成了,现在发起第三步model.chat(摘要请求(body),newStreamingChatResponseHandler(){@OverridepublicvoidonCompleteResponse(ChatResponsesummary){// 终于拿到最终结果了……// 三层嵌套,还想继续加?四层、五层……}@OverridepublicvoidonError(Throwableerror){...}});}@OverridepublicvoidonError(Throwableerror){...}});}@OverridepublicvoidonError(Throwableerror){...}});
  • 用 Future 写(桥接一次之后):
CompletableFuture<String>result=callLLM(大纲请求).thenCompose(outline->callLLM(正文请求(outline))).thenCompose(body->callLLM(摘要请求(body)));// 逻辑是一条线,从上往下读// 错误处理可以统一在最后result.exceptionally(error->"出错了: "+error.getMessage());

并行编排

并行调用ai的正确方法

// ✅ 正确:先全部发射varf1=askStream(model,q1);// 网络请求已经发出,线程去等响应了varf2=askStream(model,q2);// 第二个请求立刻发出,不用等 f1 回来varf3=askStream(model,q3);// 第三个也立刻发出// 然后才统一等待结果Stringa1=f1.join();// 如果 f1 已经回来了,瞬间拿到;没回来就等Stringa2=f2.join();Stringa3=f3.join();
  • 先全部发送再一个个join,因为join是阻塞的
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 23:09:42

模式识别电子书PDF版怎么读?从公式到代码的实战学习路径

简介&#xff1a;这份模式识别电子书PDF版面向人工智能、机器学习方向的学生与研究人员&#xff0c;用于系统梳理模式识别的基础理论与经典模型&#xff0c;尤其适合正在学习隐马尔可夫模型&#xff08;HMM&#xff09;的读者查漏补缺。资源包内共1个文件&#xff0c;为PPT格式…

作者头像 李华
网站建设 2026/10/14 21:09:32

蓝牙芯片驱动开发-第4章第2题-Bootloader如何保证固件安全性

蓝牙面试题解析:Bootloader 如何保证固件安全性? 难度:⭐⭐⭐⭐ 较难 | 场景:社招二面/三面、固件安全开发 | 高频:🔥🔥🔥🔥🔥 标准答案 Bootloader 通过 安全启动链 + 签名验证 + 存储区访问控制 三层机制保证固件加载的安全性: ① 安全启动链(Secure Boot…

作者头像 李华
网站建设 2026/10/16 14:15:53

粒子群算法优化Elman回归预测:原理、实现与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 2:28:01

蓝牙芯片驱动开发-第2章第10题-电源管理单元如何优化芯片的能耗

蓝牙面试题解析:电源管理单元如何优化芯片的能耗? 难度:⭐⭐⭐⭐ 较难 | 场景:社招二面/三面、低功耗设计 | 高频:🔥🔥🔥🔥🔥 标准答案 蓝牙芯片的电源管理单元(PMU)通过 多级电源域 + 动态电压频率调节(DVFS)+ 时钟门控 等策略实现能耗最优化: ① 多级电…

作者头像 李华