ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java Balking 模式实战:用洗衣机案例掌握并发状态守卫编程

Java Balking 模式实战:用洗衣机案例掌握并发状态守卫编程 示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载Balking犹豫/却步模式是 Java 并发领域的一种状态守卫设计模式当对象处于不完整或不合适的状态时直接拒绝执行请求的代码逻辑从而避免错误操作与并发冲突。本文以本仓库 balking 模块中的洗衣机WashingMachine案例为主线完整讲解 Balking 模式的设计意图、线程安全实现、运行效果、适用场景与权衡取舍并结合仓库源码、测试用例与运行方式给出可复制、可验证的实战方案。Balking 模式的设计意图Balking 模式的核心意图是只有当对象处于特定状态时才允许其执行某个动作否则方法直接返回、不做任何事。在 Java 多线程应用中这一模式是管理状态与并发安全的关键手段。它属于 Concurrency并发类模式兼具以下特性Decoupling解耦把「状态是否合法」的判断收敛到对象内部调用方无需关心状态细节Fault tolerance容错在状态不合适时安静返回不抛异常、不产生副作用避免级联故障Synchronization同步配合synchronized等机制保证状态检查与状态切换的原子性。Wikipedia 对该模式的经典描述是例如某个对象读取 ZIP 文件当 ZIP 文件尚未打开时调用方请求 get 方法对象就会balk拒绝这个请求——这正是 Balking 模式的行为语义。现实世界类比自助洗衣机的安全门锁想象一台投币洗衣机的场景只有当舱门正确关闭并锁定时机器才会开始洗衣。如果用户在门还开着时按下启动键机器会balk——什么都不做。这保证了洗涤过程只在安全状态下开始避免水溢出或损坏设备。软件中的 Balking 模式同理只有对象处于合适状态时才执行操作从而防止错误动作、维持系统稳定。这个类比精准概括了 Balking 模式的两个关键点前置状态检查与静默拒绝。程序化示例洗衣机如何balk本仓库 balking 模块用一个多线程洗衣机程序演示了 Balking 模式洗衣机有一个启动按钮机器空闲ENABLED时按钮正常工作机器已在洗涤WASHING时按钮按下后什么都不做。状态枚举与核心类结构WashingMachine对象仅有两种状态定义在 WashingMachineState.javapublic enum WashingMachineState { ENABLED, WASHING }从 balking.urm.puml 的 UML 结构可以看清整个模块的类关系App程序入口、WashingMachine业务主体、WashingMachineState状态枚举、DelayProvider延迟执行抽象接口其中WashingMachine聚合持有状态枚举与延迟提供者。WashingMachine 核心实现以下是 WashingMachine.java 的完整实现也是 Balking 模式的关键代码Slf4j public class WashingMachine { private final DelayProvider delayProvider; Getter private WashingMachineState washingMachineState; /** Creates a new instance of WashingMachine. */ public WashingMachine() { this( (interval, timeUnit, task) - { try { Thread.sleep(timeUnit.toMillis(interval)); } catch (InterruptedException ie) { LOGGER.error(, ie); Thread.currentThread().interrupt(); } task.run(); }); } /** * Creates a new instance of WashingMachine using provided delayProvider. * This constructor is used only for unit testing purposes. */ public WashingMachine(DelayProvider delayProvider) { this.delayProvider delayProvider; this.washingMachineState WashingMachineState.ENABLED; } /** Method responsible for washing if the object is in appropriate state. */ public void wash() { synchronized (this) { var machineState getWashingMachineState(); LOGGER.info({}: Actual machine state: {}, Thread.currentThread().getName(), machineState); if (this.washingMachineState WashingMachineState.WASHING) { LOGGER.error(Cannot wash if the machine has been already washing!); return; } this.washingMachineState WashingMachineState.WASHING; } LOGGER.info({}: Doing the washing, Thread.currentThread().getName()); this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing); } /** Method is responsible for ending the washing by changing machine state. */ public synchronized void endOfWashing() { washingMachineState WashingMachineState.ENABLED; LOGGER.info({}: Washing completed., Thread.currentThread().getId()); } }代码中蕴含三个 Balking 模式的实现要点同步块内的检查-执行check-then-actwash()在synchronized (this)块内先读取状态、再决定是否执行。将「状态检查」与「状态切换」放在同一把锁内保证多线程下检查与修改的原子性这是 Balking 模式线程安全的基础静默拒绝当状态已是WASHING时方法打印错误日志并return不执行任何洗涤逻辑也不抛异常——这正是balk的语义状态即契约endOfWashing()同样以synchronized修饰将状态回置为ENABLED与wash()形成完整的状态闭环。DelayProvider延迟执行抽象DelayProvider.java 是一个极简接口作用是模拟洗涤耗时并把「时间控制」从业务逻辑中剥离public interface DelayProvider { void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task); }该抽象有两个关键价值默认实现WashingMachine无参构造中的 Lambda通过Thread.sleep模拟 50 毫秒的洗涤耗时而测试代码则注入FakeDelayProvider将延迟完全冻结从而可以确定性、无等待地验证状态流转详见下文测试章节。应用入口与运行输出App.java 演示了真实的多线程竞争场景——用固定大小为 3 的线程池同时提交 3 次wash()调用public static void main(String... args) { final var washingMachine new WashingMachine(); var executorService Executors.newFixedThreadPool(3); for (int i 0; i 3; i) { executorService.execute(washingMachine::wash); } executorService.shutdown(); try { if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException ie) { LOGGER.error(ERROR: Waiting on executor service shutdown!); Thread.currentThread().interrupt(); } }程序运行的控制台输出来自原文档pool-1-thread-N为线程池线程名14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED 14:02:52.272 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Doing the washing 14:02:52.272 [pool-1-thread-3] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-3: Actual machine state: WASHING 14:02:52.273 [pool-1-thread-3] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing! 14:02:52.273 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-1: Actual machine state: WASHING 14:02:52.273 [pool-1-thread-1] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing! 14:02:52.324 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.输出清晰地呈现了竞争结果线程 2 抢先把状态从ENABLED置为WASHING并开始洗涤随后线程 3、线程 1 到达时发现状态已是WASHING随即 balk 拒绝50 毫秒后洗涤结束状态回到ENABLED。3 个并发请求只有 1 个真正执行这正是 Balking 模式的价值所在。源码佐证测试用例如何验证 Balking 行为仓库中的 WashingMachineTest.java 用单元测试精确验证了 Balking 语义Test void wash() { var washingMachine new WashingMachine(fakeDelayProvider); washingMachine.wash(); washingMachine.wash(); var machineStateGlobal washingMachine.getWashingMachineState(); fakeDelayProvider.task.run(); // washing machine remains in washing state assertEquals(WashingMachineState.WASHING, machineStateGlobal); // washing machine goes back to enabled state assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState()); }测试要点连续两次wash()第一次成功状态置为WASHING第二次因状态不合法而 balk印证了第二次调用不做任何事FakeDelayProvider测试用 内部类 FakeDelayProvider 捕获传入的Runnable随后手动run()触发洗涤结束从而无需真实等待即可验证状态由WASHING回落到ENABLEDendOfWashing() 测试单独调用后断言状态回到ENABLED验证状态回置逻辑AppTest.java 则通过assertDoesNotThrow(App::main)保证整个并发程序可无异常执行。如何运行本示例balking是 Maven 多模块项目 java-design-patterns 的一个子模块见 balking/pom.xml依赖 slf4j-api、logback-classic 与 JUnit 5运行方式如下# 在仓库根目录构建并运行 AppmainClass 已配置为 com.iluwatar.balking.App ./mvnw -pl balking compile exec:java # 或直接运行测试验证 Balking 行为 ./mvnw -pl balking test提示App不接收任何命令行参数见 App.java 的main(String... args)注释多线程竞争行为由代码内部固定线程池产生输出日志格式与线程调度顺序可能因机器而异但仅一个线程成功洗涤、其余 balk的最终结论不变。何时使用 Balking 模式适合采用 Balking 模式的情况仅在对象处于特定状态时执行动作动作的合法性完全由对象当前状态决定对象的不合法状态是临时性的但持续时间不确定例如资源初始化中、连接建立中、任务执行中等外部因素或并发操作会让状态随时间变化多线程应用中动作需要满足特定条件才能继续而这些条件会因外部因素或并发操作而动态变化。典型应用场景来自原文档资源池Resource pooling仅在资源处于可分配的有效状态时才进行分配线程管理Thread management仅当任务可用、资源锁满足等特定条件成立时线程才继续执行任务。收益与权衡收益减少不必要的锁竞争在动作无法推进时直接返回避免无意义地长时间占用锁提升并发应用的吞吐与性能职责分离更清晰状态管理与行为逻辑被明确分离代码更干净、可读性更强调用方更简洁把「仅在某些条件下执行」的复杂判断收敛到对象内部调用方代码无需堆砌大量状态检查。权衡隐式条件增加调试难度动作何时执行、何时被忽略的条件被封装在对象内部可能让系统更难调试和理解可能错失动作机会如果状态变化没有被恰当监控或 balk 条件设置得过于严格可能导致应该执行的动作被跳过。与相关 Java 设计模式的关联Balking 模式常与以下模式配合或对照使用Double-Checked Locking确保初始化只在必要时发生并避免不必要的加锁。与 Balking 一样都依据对象状态来条件性执行逻辑Guarded Suspension同样保证动作只在对象处于特定状态时执行但 Guarded Suspension 通常会让线程等待状态变为有效而 Balking 是直接放弃StateState 模式可用来管理对象的全部状态与状态转移与 Balking 结合使用可以更规范地组织状态机逻辑。理解三者的差异非常关键Balking 是状态不合适就立即返回Guarded Suspension 是状态不合适就等待二者分别对应拒绝与阻塞两种策略可根据业务对时延与成功率的诉求取舍。小结通过本仓库 balking 模块的洗衣机案例我们完整掌握了 Balking 模式以synchronized保证状态检查-执行原子性、状态不合法时静默返回、再配合DelayProvider解耦耗时逻辑最后用线程池制造真实竞争、用单元测试锁定行为语义。在多线程 Java 应用中当你想让某个操作只在对象处于合适状态时发生Balking 就是最简单直接的并发守卫方案。赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐Feature Walkthrough 实操指南在 Civitai 仓库用真实截图把功能走查变成可分享的演示页Feature Walkthrough 实操指南在 Civitai 仓库用真实截图把功能走查变成可分享的演示页 本文讲解 Civitai 仓库 .claude示例工程教程Transitions完全教程10个实战案例教你掌握状态机编程Transitions完全教程10个实战案例教你掌握状态机编程 状态机编程是现代软件开发中的重要概念而Transitions库正是Python领域中最强大的后端流程编排Chemprop实际应用案例从抗生素发现到ADMET预测的成功实践Chemprop实际应用案例从抗生素发现到ADMET预测的成功实践 Chemprop是一个基于PyTorch的分子性质预测框架通过消息传递神经网络MPNN人工智能机器学习深度学习科研上一篇TypeScript 类型合并Merging与扩展Extension实战指南来自 The Concise TypeScript Book 的权威解读下一篇免费解锁WeMod高级功能Wand-Enhancer完整使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表