编辑
🌟 大家好,我是摘星! 🌟
今天为大家带来的是并发设计模式实战系列,第10章Balking(犹豫模式),废话不多说直接开始~
目录
2. Balking + Chain of Responsibility
一、核心原理深度拆解
1. 状态守护机制
┌───────────────┐ ┌───────────────┐ │ Client │──────>│ GuardedObj │ └───────────────┘ ├───────────────┤ │ - state │ │ - checkState()│ │ - changeState() └───────────────┘
- 状态检查:通过原子操作验证对象是否处于可处理状态
- 条件拦截:当状态不满足时立即放弃操作(Balking)
- 线程安全:所有状态变更必须加锁(synchronized或CAS)
2. 与状态模式的区别
- Balking:直接放弃不符合条件的请求(快速失败)
- State Pattern:将行为委托给不同状态对象处理
二、生活化类比:自动售货机
系统组件 |
现实类比 |
核心行为 |
Client |
顾客投币 |
发起购买请求 |
checkState |
货品检测系统 |
检查库存和金额是否充足 |
Balking |
退币机制 |
条件不满足时立即退币 |
- 典型场景:当检测到「缺货」或「金额不足」时,立即终止交易流程
三、Java代码实现(生产级Demo)
1. 完整可运行代码
import java.util.concurrent.atomic.*; // 带Balking模式的文件自动保存 public class AutoSaveEditor { // 状态标记(原子操作) private final AtomicBoolean changed = new AtomicBoolean(false); private final AtomicInteger autoSaveCount = new AtomicInteger(0); // 状态守护方法 public void autoSave() { // STEP 1: 状态检查 if (!changed.getAndSet(false)) { System.out.println("[Balking] 无修改不保存"); return; // 快速返回 } // STEP 2: 实际保存操作 doSave(); } // 修改内容后触发状态变更 public void editDocument(String newContent) { System.out.println("编辑内容: " + newContent); changed.set(true); } private void doSave() { System.out.println("自动保存第" + autoSaveCount.incrementAndGet() + "次..."); // 模拟IO操作 try { Thread.sleep(500); } catch (InterruptedException e) {} } // 测试用例 public static void main(String[] args) throws InterruptedException { AutoSaveEditor editor = new AutoSaveEditor(); // 第一次修改(应触发保存) editor.editDocument("Version1"); editor.autoSave(); // 连续修改不保存(状态被消费) editor.editDocument("Version2"); editor.editDocument("Version3"); editor.autoSave(); // 只会保存一次 // 无修改情况 editor.autoSave(); } }
2. 关键实现技术
// 1. 原子状态标记 private final AtomicBoolean changed = new AtomicBoolean(false); // 2. 状态检查与重置一体化操作 changed.getAndSet(false) // 3. 线程安全计数器 autoSaveCount.incrementAndGet()
四、横向对比表格
1. 相似模式对比
模式 |
核心策略 |
适用场景 |
Balking |
条件不满足立即放弃 |
状态校验场景(如自动保存) |
Retry |
条件不满足循环重试 |
网络请求等可恢复场景 |
State |
委托给状态对象处理 |
复杂状态转换场景 |
Guard Suspension |
等待条件满足 |
必须完成的阻塞任务 |
2. 线程安全方案选择
实现方式 |
优点 |
缺点 |
synchronized |
简单可靠 |
性能开销较大 |
AtomicXXX |
无锁高性能 |
只适用于简单状态 |
ReentrantLock |
可中断/超时 |
需手动释放锁 |
volatile |
轻量级可见性保证 |
不保证复合操作原子性 |
五、高级应用技巧
1. 组合模式增强
// 结合Guard Suspension模式实现超时控制 public boolean autoSaveWithTimeout(long timeout) throws InterruptedException { long start = System.currentTimeMillis(); while (!changed.get()) { if (System.currentTimeMillis() - start > timeout) { return false; // 超时放弃 } Thread.sleep(50); } return doSave(); }
2. 日志增强实现
// 记录Balking事件 if (!changed.get()) { auditLogger.log("Balking at " + LocalDateTime.now()); return; }
3. Spring应用场景
@Component public class ConfigMonitor { @Scheduled(fixedRate = 5000) public void reloadConfig() { if (!GlobalConfig.isDirty()) { return; // Balking } // 重新加载配置... } }
好的!我将延续原有结构,从 第六部分 开始扩展Balking模式的深度内容,保持技术解析的连贯性和完整性。
六、Balking模式变体与扩展(续)
1. 分布式场景下的Balking
// 使用Redis实现分布式状态标记 public class DistributedBalking { private final Jedis jedis; private static final String LOCK_KEY = "resource:lock"; public boolean tryProcess(String resourceId) { // SETNX实现原子状态检查(分布式锁原理) Long result = jedis.setnx(LOCK_KEY, "locked"); if (result == 0) { System.out.println("[Distributed Balking] 资源已被占用"); return false; } jedis.expire(LOCK_KEY, 30); return true; } }
关键点:
- 使用Redis的
SETNX
命令替代本地原子变量 - 需设置过期时间避免死锁
- 适用于微服务抢单、定时任务调度等场景
2. 分级Balking策略
// 根据业务重要性实现分级放弃 public class PriorityBalking { private enum Priority { HIGH, NORMAL, LOW } public void process(Priority priority) { if (!checkResource()) { if (priority == Priority.LOW) { System.out.println("低优先级任务放弃"); return; } // 高优先级任务等待资源 waitForResource(); } // 执行处理... } }
七、性能优化与陷阱规避
1. 状态检查的性能优化
优化手段 |
实现方式 |
适用场景 |
双重检查锁 |
先非阻塞检查,再同步块内二次检查 |
高并发读场景 |
状态标记分组 |
对不同资源分桶标记 |
多资源竞争场景 |
延迟状态重置 |
处理完成后再重置状态(减少CAS竞争) |
短时高频状态变更 |
// 双重检查锁实现示例 public class DoubleCheckBalking { private volatile boolean busy = false; public void execute() { if (!busy) { // 第一次非阻塞检查 synchronized (this) { if (!busy) { // 第二次原子检查 busy = true; // 执行任务... busy = false; } } } } }
2. 常见陷阱与解决方案
陷阱现象 |
根本原因 |
解决方案 |
活锁(Livelock) |
多个线程持续检查-放弃循环 |
引入随机退避时间 |
状态逃逸 |
对象引用被外部修改 |
防御性拷贝(Deep Copy) |
监控缺失 |
无法追踪放弃操作次数 |
添加Metrics计数器 |
八、工业级应用案例
1. Tomcat连接器中的Balking
// org.apache.tomcat.util.net.AbstractEndpoint public boolean processSocket(SocketWrapperBase<S> socket) { if (running && !paused) { // 将socket交给线程池处理 return executor.execute(new SocketProcessor(socket)); } // 服务未运行立即放弃 return false; }
设计启示:
- 通过
running
和paused
双状态判断 - 放弃时直接关闭Socket连接释放资源
2. 电商库存扣减场景
public class InventoryService { private final AtomicInteger stock = new AtomicInteger(100); public boolean deductStock(int quantity) { int current = stock.get(); if (current < quantity) { // 库存不足立即返回 metrics.log("balking:insufficient_stock"); return false; } // CAS原子扣减 return stock.compareAndSet(current, current - quantity); } }
九、与其他模式的组合应用
1. Balking + Observer 模式
public class ConfigMonitor { private final List<Listener> listeners = new CopyOnWriteArrayList<>(); private volatile String currentConfig; // 配置变更通知(Balking条件检查) public void updateConfig(String newConfig) { if (Objects.equals(currentConfig, newConfig)) { return; // 配置未变化时放弃 } this.currentConfig = newConfig; notifyListeners(); } }
2. Balking + Chain of Responsibility
public abstract class OrderHandler { private OrderHandler next; public void handle(Order order) { if (canHandle(order)) { // 实际处理逻辑... } else if (next != null) { next.handle(order); } else { // 责任链终止时的Balking order.fail("NO_HANDLER_FOUND"); } } protected abstract boolean canHandle(Order order); }
十、终极对比表格:Balking模式家族
变体名称 |
核心差异点 |
典型应用场景 |
Java SDK中的体现 |
经典Balking |
基于本地原子变量 |
单机资源控制 |
AtomicBoolean.getAndSet |
分布式Balking |
依赖外部存储状态 |
跨服务协调 |
Redis SETNX |
分级Balking |
按优先级差异化处理 |
业务流量分级 |
ThreadPoolExecutor拒绝策略 |
延迟Balking |
超时后才放弃 |
弱依赖服务调用 |
Future.get(timeout) |
批量Balking |
累积多个请求后统一判断 |
批量处理系统 |
BufferedWriter.flush |