Half-Sync/Half-Async 并发模式 Java 实战:基于 java-design-patterns 的异步层与同步层解耦实现解析
示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载本篇技术指南围绕 java-design-patterns 仓库中的half-sync-half-async模块系统讲解 Half-Sync/Half-Async半同步/半异步并发设计模式的原理与 Java 实现。通过阅读本文你将掌握该模式异步层接收请求、队列层解耦缓冲、同步层阻塞处理的三层协作模型理解AsyncTask生命周期回调与ThreadPoolExecutor结合的实现细节并学会在混合长短任务的并发系统中正确落地这一模式。模式概览别名与设计意图Half-Sync/Half-Async 模式在并发领域还有两个常见别名Async-Sync Bridge异步-同步桥Half-Synchronous/Half-Asynchronous半同步/半异步其核心设计意图Intent十分明确在并发系统中解耦异步处理与同步处理从而提升系统的效率与性能。该模式特别适用于管理软件系统中复杂的并发操作场景——当系统同时存在短、中、长三种耗时的任务时如果不加区分地全部同步执行主线程将被长任务长期占用而全部异步化又会引入复杂的状态管理与回调地狱。Half-Sync/Half-Async 通过引入两个相互通信的处理层在简化编程模型与不影响性能之间取得平衡。现实世界类比忙碌的餐厅厨房想象一家繁忙的餐厅厨房点单是异步的服务员无需等待某道菜做完就能继续接待下一桌客人而烹饪是同步的厨师需要按特定顺序、等每道菜准备完成后才能开始下一道。这种设置让餐厅能高效处理多位顾客的点单同时保证每道菜都得到应有的烹制时间——正如 Half-Sync/Half-Async 模式在软件系统中管理异步任务与同步处理一样。用一句大白话概括Half-Sync/Half-Async 将操作分为两类——异步任务负责不等待地接收和处理事件同步任务则有序、阻塞地处理这些事件。Wikipedia 对它的权威定义是Half-Sync/Half-Async 设计模式用于解决一类场景应用的一部分以同步方式运行另一部分以异步方式运行而这两个模块之间需要相互通信。架构剖析异步层、队列层与同步层的三层协作从模式名即可看出系统被拆分为两个处理层但实际落地时通常还隐含一个队列层作为两层之间的通信媒介。下图清晰展示了请求在模式中的完整流转路径时序图揭示了完整的调用链Client向AsyncLayer异步层发送handleRequest()请求AsyncLayer立即返回不阻塞客户端同时调用enqueue(request)将请求放入Queue队列层SyncLayer同步层从队列调用dequeue()取出请求同步层将请求交给Worker工作线程执行process(request)Worker完成后返回done同步层最终向Client返回response。在这一模型中异步层负责接收与分发保证系统的响应性队列层负责解耦缓冲允许两层以不同节奏协作同步层负责阻塞式的有序处理保证任务质量。值得注意的是队列层本身是可替换的通信策略载体——使用不同的队列类型即可改变两层之间的通信模式详见下文队列层变体。源码级实现从 App 入口到 AsynchronousService仓库中该模式由 3 个关键类构成入口类App、异步服务AsynchronousService与任务抽象接口AsyncTask。类结构关系如下图所示入口 App提交任务而不阻塞主线程App.java 是程序入口。它的核心演示点在于主线程只是把任务丢给异步层然后立即继续自己的工作就像网络服务器在Socket上等待新请求时不会阻塞等待某个具体请求完成一样。public static void main(String[] args) { var service new AsynchronousService(new LinkedBlockingQueue()); service.execute(new ArithmeticSumTask(1000)); service.execute(new ArithmeticSumTask(500)); service.execute(new ArithmeticSumTask(2000)); service.execute(new ArithmeticSumTask(1)); service.close(); }四个任务求和项数分别为 1000、500、2000、1被连续提交execute方法立即返回主线程不等待任何计算结果。随后通过close()优雅关闭服务。AsyncTask 接口任务的四个生命周期回调AsyncTask.java 定义了任务抽象它继承CallableO并额外声明了三个生命周期回调形成预处理 → 执行 → 后处理 → 异常兜底的完整任务模型方法调用上下文职责onPreCall()调用者主线程执行轻量校验等小任务避免在后台线程中做校验而付出上下文切换代价call()后台工作线程真正的计算逻辑所在属于长耗时任务onPostCall(O result)后台工作线程本实现计算结果成功后的回调onError(Throwable)后台工作线程当call()或onPreCall()抛出异常时调用该接口刻意不提供isComplete、cancel等方法因为这些超出了本模式的范围。AsynchronousService异步层的核心引擎AsynchronousService.java 是模式的执行中枢其注释明确描述了分层职责异步层在收到新请求时不阻塞直接把请求交给由BlockingQueue与ThreadPoolExecutor构成的同步层线程池中的某个工作线程在后台同步取出并执行任务结果通过回调回传给调用者。构造时它通过ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, workQueue)创建一个核心线程数与最大线程数均为 10、空闲线程存活 10 秒的固定规模线程池并将外部传入的BlockingQueue作为线程池的任务队列——这个队列正是异步层与同步层之间的通信信道public AsynchronousService(BlockingQueueRunnable workQueue) { service new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, workQueue); }execute方法实现了非阻塞提交 回调反馈的核心逻辑public T void execute(final AsyncTaskT task) { try { // 一些小的任务如校验可以在这里执行 task.onPreCall(); } catch (Exception e) { task.onError(e); return; } service.submit( new FutureTask(task) { Override protected void done() { super.done(); try { task.onPostCall(get()); } catch (InterruptedException e) { // 不应发生 } catch (ExecutionException e) { task.onError(e.getCause()); } } }); }关键机制拆解同步预校验onPreCall()在提交线程上下文中同步执行。若校验失败抛异常直接调用onError并返回任务根本不会进入线程池——这样无效请求就不会占用线程资源。FutureTask done() 钩子任务被包装为FutureTask提交到线程池并覆写done()方法。done()在线程池中的任务执行完毕后由后台工作线程调用此时通过get()取出结果并触发onPostCall若执行期间抛异常则触发onError(e.getCause())。线程上下文说明本实现中回调都在后台线程上下文中执行。源码注释指出存在另一种变体——结果先放入调用者线程的队列由调用者线程取出处理Android 系统即属此类因为 UI 元素只能由 UI 线程更新结果必须回投到 UI 线程。close()方法则负责优雅关闭先shutdown()停止接收新任务再awaitTermination(10, TimeUnit.SECONDS)阻塞等待所有已提交任务完成超时或中断时记录错误日志。完整任务示例ArithmeticSumTaskApp.java 内部定义的ArithmeticSumTask是一个完整的AsyncTaskLong实现用于演示如何自定义任务的生命周期行为static class ArithmeticSumTask implements AsyncTaskLong { private final long numberOfElements; public ArithmeticSumTask(long numberOfElements) { this.numberOfElements numberOfElements; } Override public Long call() throws Exception { return ap(numberOfElements); } Override public void onPreCall() { if (numberOfElements 0) { throw new IllegalArgumentException(n is less than 0); } } Override public void onPostCall(Long result) { LOGGER.info(result.toString()); } Override public void onError(Throwable throwable) { throw new IllegalStateException(Should not occur); } }其call()委托给私有静态方法ap(long i)先Thread.sleep(i)模拟长耗时任务再返回等差数列求和结果i * (i 1) / 2。onPreCall中校验numberOfElements 0时抛出IllegalArgumentException展示同步层内的输入校验onError直接抛出IllegalStateException声明在此示例中错误不应发生。队列层变体通信模式的可替换设计源码注释特别强调队列层是模式中允许差异化的部分使用不同类型的BlockingQueue即可改变两层间的通信模式。例如使用LinkedBlockingQueue本示例默认实现 FIFO 公平调度使用PriorityBlockingQueue作为队列层即可按任务优先级调整执行顺序使用有界队列可施加背压防止请求洪峰压垮同步层。这一点让模式具备很强的适应性——队列层既是解耦缓冲也是可插拔的调度策略载体。运行与验证测试用例与运行输出测试用例验证回调顺序AsynchronousServiceTest.java 使用 Mockito 编写了三个用例精确验证了execute的回调调用序列testPerfectExecution成功路径下调用顺序严格为onPreCall() → call() → onPostCall(result)且onPostCall收到的参数与call()返回值一致testCallException当call()抛出IOException时顺序变为onPreCall() → call() → onError(exception)testPreCallException当onPreCall()抛出异常时顺序变为onPreCall() → onError(exception)且call()根本不会被调用——验证了同步预校验短路逻辑。AppTest.java 则验证App.main(null)可无异常执行完毕。运行输出运行该示例任务含Thread.sleep人工延迟会产生类似如下的日志输出可以看到四个任务在pool-1-thread-*多个后台线程中并行完成且求和结果正确1000→500500500→1252502000→20010001→110:56:33.922 [pool-1-thread-4] INFO com.iluwatar.halfsynchalfasync.App -- 1 10:56:34.425 [pool-1-thread-2] INFO com.iluwatar.halfsynchalfasync.App -- 125250 10:56:34.925 [pool-1-thread-1] INFO com.iluwatar.halfsynchalfasync.App -- 500500 10:56:35.925 [pool-1-thread-3] INFO com.iluwatar.halfsynchalfasync.App -- 2001000输出清晰地印证了任务入队后由工作线程并行异步处理的行为小任务率先完成大任务在各自线程中按延迟时长陆续收尾而主线程全程未被阻塞。如何运行该模块是 Maven 多模块项目java-design-patterns版本 1.26.0-SNAPSHOT下的独立子模块pom.xml 通过maven-assembly-plugin将主类配置为com.iluwatar.halfsynchalfasync.App依赖包括slf4j-api、logback-classic、junit-jupiter-engine与mockito-core后两者为测试依赖。可以从仓库根目录使用./mvnw -pl half-sync-half-async test运行模块测试或在模块内执行./mvnw test验证测试用例。何时使用 Half-Sync/Half-Async 模式在 Java 项目中以下场景适合采用该模式对高性能与高效并发有硬性要求如 Java 标准库及处理海量并发连接的网络服务器需要充分利用多核架构在异步处理与同步处理之间合理均衡任务分配发挥多核并行能力需要解耦异步任务与同步处理通过分层隔离来简化设计与实现避免在单一代码路径中混用阻塞与非阻塞逻辑。真实世界应用该模式在业界有广泛且经过验证的应用案例BSD Unix 网络子系统操作系统层面的网络操作借助硬件级中断以异步方式完成请求处理则同步进行Real-Time CORBA异步层为每个连接客户端的 Socket 关联一个线程线程阻塞等待 CORBA 请求收到请求后插入队列层由同步层取出处理并回送响应Android AsyncTask 框架提供在后台线程执行长阻塞调用如下载文件的能力使 UI 线程保持空闲以响应交互Java 标准库java.util.concurrent中的线程池与执行队列如ThreadPoolExecutor、BlockingQueue正是该模式的典型体现网络服务器IO 操作异步处理、请求处理同步执行两者通过队列衔接。优点与权衡优点将阻塞操作与非阻塞操作隔离显著提升系统响应性与吞吐量通过隔离异步/同步两个处理层简化了编程模型——调用方无需关心阻塞细节。权衡管理两种处理模式本身会增加系统复杂度需要精心设计避免同步部分与异步部分之间出现瓶颈例如队列积压或线程池耗尽导致的反压问题。与相关模式的对比Leader/Followers两者都涉及线程分配与并发管理但 Leader/Followers 使用单个线程处理所有 IO 事件再把工作分派给其他线程Half-Sync/Half-Async 则通过队列在两层间解耦。Producer/Consumer可与 Half-Sync/Half-Async 集成用于管理异步部分与同步部分之间的工作队列——异步层充当生产者同步层充当消费者。Reactor常与 Half-Sync/Half-Async 配合使用前者负责将多个服务请求分发给服务处理器而不阻塞处理器后者负责解耦处理过程中的同步与异步环节。参考资料与延伸阅读Java Concurrency in PracticeBrian Goetz 等著Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked ObjectsDouglas Schmidt 等Half-Sync/Half-AsyncDouglas C. Schmidt 与 Charles D. Cranor 的经典论文如需进一步深入可直接阅读本模块的源码与测试核心实现位于 AsynchronousService.java 与 AsyncTask.java完整示例见 App.java行为验证见 AsynchronousServiceTest.java。赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐Java 事件驱动异步模式Event-Based Asynchronous Pattern实战解析基于 java-design-patterns 的完整实现Java 事件驱动异步模式Event Based Asynchronous Pattern实战解析基于 java design patterns 的完整实示例工程教程Java 设计模式实战Async Method Invocation 异步方法调用模式在 java-design-patterns 中的实现Java 设计模式实战Async Method Invocation 异步方法调用模式在 java design patterns 中的实现 异步方法调用A示例工程教程Java 异步方法调用Async Method Invocation模式详解基于 java-design-patterns 的源码级剖析Java 异步方法调用Async Method Invocation模式详解基于 java design patterns 的源码级剖析 异步方法调用A示例工程教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →