示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载导读本文以开源仓库 java-design-patterns 中的active-object模块为实践蓝本系统讲解 Active Object主动对象设计模式——一种把方法调用与方法执行彻底解耦的并发模式。你将掌握该模式的六大构成要素、基于BlockingQueue的消息队列调度原理、完整的可运行代码示例含兽人 Orc 演示以及它与 Command、Promise、Proxy 等模式的关联。读完后你能独立在 Java 项目中落地一个线程安全、主线程不阻塞的异步任务处理方案。一、模式意图让每个对象拥有自己的控制线程Active Object 模式的核心意图非常明确将方法执行与方法的调用分离开来让每个对象都运行在各自独立的控制线程thread of control中。它通过异步方法调用 调度器scheduler引入并发使调用方线程发出请求后无需等待执行完成即可继续做自己的事。在 active-object/README.md仓库英文原版文档中该模式被描述为通过把任务封装进自带线程与消息队列的对象使主线程保持响应同时规避直接操作线程和共享状态访问带来的并发问题。从实现层面看App.java 的类注释还给出了一个通俗解释Active Object 模式帮助我们在不写synchronized方法的前提下解决同步难题——主动对象内部持有一个线程安全的数据结构如BlockingQueue通过把方法逻辑移入一个调用器通常是Runnable并存入该数据结构来完成方法调用的同步。这正是由数据结构代替锁的经典思路。现实世界类比餐馆里的下单与出餐想象一家繁忙的餐厅顾客向服务员下单服务员并不亲自进厨房烹饪而是把订单写在纸条上交给调度员。调度员管理着一组厨师厨师们异步地准备餐食——哪位厨师空闲就取下一条订单做好菜后通知服务员上菜。在这里服务员代表调用方线程调度员代表调度器厨师代表在独立线程中执行的方法。服务员可以不停接单完全不被烹饪过程阻塞。这个类比精准对应了 Active Object 的三大角色调用方Client→ 调度器Scheduler→ 执行方Servant/方法实现。仓库文档用它来说明方法调用与执行解耦如何提升并发度。来自 Wikipedia 的六要素定义模式由六个要素组成见 active-object/README.md要素职责Proxy代理向客户端提供公开可访问的方法接口Method Request方法请求接口定义作用于主动对象的方法请求Activation List待处理请求列表保存来自客户端的待执行请求队列Scheduler调度器决定下一步执行哪一个请求Servant实现主动对象方法的实际实现Callback回调/变量客户端用来接收执行结果在仓库示例中ActiveCreature.eat()/roam()是公开接口Proxy 层LinkedBlockingQueueRunnable是 Activation List专用线程内的requests.take()循环是 SchedulerRunnable 内部的日志逻辑是 Servant 实现。二、仓库实战示例让兽人拥有自己的控制线程文档以兽人Orcs以狂野不驯著称似乎拥有自己独立的行为控制线程为引子演示如何实现一个拥有自控线程、只暴露 API 而不暴露执行过程的生物。2.1 基类 ActiveCreature线程 队列 调度循环完整代码见 ActiveCreature.java其核心结构如下public abstract class ActiveCreature { private static final Logger logger LoggerFactory.getLogger(ActiveCreature.class.getName()); private BlockingQueueRunnable requests; // 待执行请求队列Activation List private String name; private Thread thread; // 执行线程thread of control private int status; // 执行线程状态 /** 构造函数初始化队列、创建并启动专属线程 */ protected ActiveCreature(String name) { this.name name; this.status 0; this.requests new LinkedBlockingQueue(); thread new Thread( () - { boolean infinite true; while (infinite) { try { requests.take().run(); // 取出一个请求并执行Scheduler Servant } catch (InterruptedException e) { if (this.status ! 0) { logger.error(Thread was interrupted. -- {}, e.getMessage()); } infinite false; Thread.currentThread().interrupt(); } } }); thread.start(); } /** 进食把进食逻辑封装成 Runnable 投入队列立即返回 */ public void eat() throws InterruptedException { requests.put( () - { logger.info({} is eating!, name()); logger.info({} has finished eating!, name()); }); } /** 游荡同上异步投入队列 */ public void roam() throws InterruptedException { requests.put(() - logger.info({} has started to roam in the wastelands., name())); } public String name() { return this.name; } /** 终止执行线程status 0 表示正常退出非 0 则记录错误 */ public void kill(int status) { this.status status; this.thread.interrupt(); } public int getStatus() { return this.status; } }这段源码值得逐点拆解LinkedBlockingQueueRunnable是无界阻塞队列put()永不因队列满而失败调用方线程把Runnable投入队列后立刻返回不阻塞而执行线程用take()在队列为空时阻塞等待实现有活就干、没活休眠的优雅调度。调度循环即 Schedulerwhile(true) { requests.take().run(); }以**先入先出FIFO**顺序消费队列同一对象的所有方法请求被其专属线程串行执行天然避免了共享状态竞争——这正是不用 synchronized 也能线程安全的根源。中断处理kill(int status)通过thread.interrupt()让阻塞中的take()抛出InterruptedException线程据此退出循环并重置中断标志Thread.currentThread().interrupt()。状态位status用于区分正常退出0与异常中断非 0中断时仅在异常情况下记录错误日志。API 与执行分离eat()、roam()只是投递请求真正的执行发生在专属线程内部客户端看不到也控制不了执行时机。2.2 子类 Orc一行继承即可复用全部并发机制public class Orc extends ActiveCreature { public Orc(String name) { super(name); } }完整源码见 Orc.java。任何继承ActiveCreature的类都会自动获得自己的控制线程——这印证了文档的结论子类只需关注业务行为并发调度完全由基类封装。2.3 启动入口 App创建多个兽人并发调度public class App implements Runnable { private static final Logger logger LoggerFactory.getLogger(App.class.getName()); private static final int NUM_CREATURES 3; public static void main(String[] args) { var app new App(); app.run(); } Override public void run() { ListActiveCreature creatures new ArrayList(); try { for (int i 0; i NUM_CREATURES; i) { creatures.add(new Orc(Orc.class.getSimpleName() i)); creatures.get(i).eat(); creatures.get(i).roam(); } Thread.sleep(1000); } catch (InterruptedException e) { logger.error(e.getMessage()); Thread.currentThread().interrupt(); } finally { for (int i 0; i NUM_CREATURES; i) { creatures.get(i).kill(0); } } } }完整源码见 App.java。注意两点差异文档示例中的常量名为creatures仓库实际源码为NUM_CREATURES 3并以Orc.class.getSimpleName() i即Orc0、Orc1、Orc2命名每个实例仓库源码比文档示例多了finally块中调用kill(0)优雅关闭各线程的收尾逻辑——这是更严谨的生产级写法避免线程泄漏。运行入口由 active-object/pom.xml 的maven-assembly-plugin配置指定为com.iluwatar.activeobject.App可用./mvnw -pl active-object -am compile exec:java或打包后运行 jar 复现。2.4 运行输出见证真正的并发执行文档给出的程序输出如下09:00:02.501 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 is eating! 09:00:02.501 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 is eating! 09:00:02.501 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 is eating! 09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has finished eating! 09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has finished eating! 09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has started to roam in the wastelands. 09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has finished eating! 09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has started to roam in the wastelands. 09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has started to roam in the wastelands.这段输出极具教学价值可以提炼三个关键观察每个兽人拥有独立线程日志中的Thread-0/1/2分别对应Orc0/1/2的专属控制线程方法调用立即返回、执行异步发生三只兽人的eat()在同一毫秒.501内全部投递完成而正在吃/吃完了的执行日志在take().run()的调度下交错出现同对象内请求按 FIFO 串行执行对每个线程而言开始吃必然先于吃完先于开始游荡单个主动对象内部严格有序不同对象之间天然并行。三、类图与结构剖析上图对应的 PlantUML 源文件位于 active-object/etc/active-object.urm.puml其继承关系清晰可见Orc --| ActiveCreature即Orc泛化自抽象类ActiveCreature。从类图可以提炼出该模块的完整结构与文档六要素一一对应ActiveCreature抽象类持有BlockingQueueRunnable requestsActivation List、Thread threadScheduler 运行载体、name与logger公开eat()、roam()、name()Orc具体主动对象仅实现构造函数复用基类全部并发能力App客户端持有兽人列表与日志器负责创建实例、投递请求、休眠等待并最终关闭线程。仓库另提供一张时序图 active-object/etc/active-object-sequence-diagram.png展示了App → ActiveCreature → Thread → Runnable之间的异步调用时序可用于理解请求从投递到执行的完整生命周期。四、模式在 Java 中的适用场景文档明确指出以下情况适合引入 Active Object 模式需要在不让主线程阻塞的前提下处理异步任务从而提升性能与响应性需要异步地与外部资源交互如网络请求、文件 IO、消息中间件想提升应用的整体响应速度希望以模块化、可维护的方式管理并发任务。结合现实场景文档列举了三类典型应用应用领域具体表现实时交易系统交易请求异步处理避免阻塞主交易流程GUI 桌面/移动应用长耗时任务在后台线程执行界面不冻结游戏编程并发更新游戏状态或执行 AI 计算值得一提的是App.java 的类注释提到本示例会启动 20 个线程去修改目标类中的某个值说明该模式在仓库中还被用于演示多线程并发修改共享值时如何借助主动对象避开显式加锁不过当前App实际实现以 3 只兽人NUM_CREATURES进行演示读者可自行调整该常量观察线程数量对输出的影响。五、优点与代价权衡优点提升主线程响应性调用方发完请求即可继续无需等待任务完成并发关注点被封装在对象内部调用方无需接触线程、锁或队列细节代码组织更清晰、更易维护每个主动对象是自包含的并发单元天然线程安全请求经队列串行化避免了共享状态访问问题这正是免synchronized方案的价值所在。代价引入消息传递与线程管理的额外开销每个主动对象都要消耗一个线程与一个队列任务粒度较小时成本占比明显并非适用于所有并发问题对于高吞吐、低延迟、任务极短的场景线程切换与队列操作的开销可能超过收益。适用前提与限制基于仓库实现推断本示例中每个ActiveCreature固定占用一个常驻线程守护循环若有 N 个对象即占用 N 个线程对象数量巨大时需评估线程资源上限LinkedBlockingQueue为无界队列若生产速度长期大于消费速度内存可能持续增长生产环境可考虑替换为有界队列并配合拒绝策略。六、验证测试用例如何证明模式行为仓库提供了 ActiveCreatureTest.java 与 AppTest.java 两个测试类其中前者直接验证了模式的三大行为Test void executionTest() throws InterruptedException { ActiveCreature orc new Orc(orc1); assertEquals(orc1, orc.name()); // ① 名称正确 assertEquals(0, orc.getStatus()); // ② 初始线程状态为 0正常 orc.eat(); // ③ 投递进食请求 orc.roam(); // ④ 投递游荡请求 orc.kill(0); // ⑤ 正常关闭执行线程 }这个测试揭示了三个可验证的事实eat()、roam()、kill(0)均声明throws InterruptedException调用方在InterruptedException场景下应妥善处理如App中记录日志并Thread.currentThread().interrupt()恢复中断标志kill(0)的语义是以正常状态关闭线程配合基类getStatus()可断言线程退出状态测试通过投递即返回、执行在线程内异步发生的模型验证了调用与执行解耦的核心特性。七、与相关模式的关联文档将 Active Object 与以下三个模式建立了关联理解它们的区别有助于在实际项目中选型Command 模式同样把请求封装为对象。Active Object 本质上是Command 异步调度的组合——Runnable请求就是命令对象调度线程负责择机执行Promise 模式提供获取异步调用结果的手段常与 Active Object 搭配使用主动对象执行完成后通过 Promise/回调把结果回传调用方Proxy 模式Active Object 可借助代理对象在调用方与执行方之间拦截方法调用实现异步投递。仓库中的 Active Object 通过继承基类直接暴露接口同样达到了隐藏执行细节的代理效果。八、小结Active Object 模式用对象自带控制线程 阻塞队列调度这一朴素而强大的设计在 Java 中优雅地解决了异步与线程安全两大并发难题。通过本文对仓库active-object模块的源码级剖析你可以清楚地看到调用与执行解耦eat()/roam()只投递、不执行执行权完全交给对象专属线程免锁线程安全单对象内的请求经LinkedBlockingQueueFIFO 串行化规避了共享状态竞争优雅生命周期kill(status) 中断标志复位让线程可受控退出测试可验证仓库测试用例直接断言了投递-执行-关闭的完整流程。进一步探索可参考仓库中的 active-object 英文原版文档、类图源文件 active-object.urm.puml、核心实现 ActiveCreature.java 与测试 ActiveCreatureTest.java。若想系统学习并发模式仓库中同属 Concurrency 分类的 Promise、Producer-Consumer、Guarded Suspension、Thread Pool Executor 等模块值得继续研读。赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐Active Object 模式实战基于 java-design-patterns 的 Java 异步方法调用与调度机制详解Active Object 模式实战基于 java design patterns 的 Java 异步方法调用与调度机制详解 本文以 java design示例工程教程java-design-patterns 项目实战Active Object 主动对象模式——在 Java 中实现异步调用与自同步并发java design patterns 项目实战Active Object 主动对象模式——在 Java 中实现异步调用与自同步并发 本文基于 java d示例工程教程Java 异步方法调用Async Method Invocation模式详解基于 java-design-patterns 的源码级剖析Java 异步方法调用Async Method Invocation模式详解基于 java design patterns 的源码级剖析 异步方法调用A示例工程教程上一篇MiniMax-M2.7-MXFP4 vs 原版模型GSM8K基准测试91.89%准确率背后的技术突破 下一篇5分钟解锁金融数据自由MOOTDX让通达信数据触手可及创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?