大家好,我是摘星!
今天为大家带来的是并发设计模式实战系列,第10章Balking(犹豫模式),废话不多说直接开始~
目录
一、核心原理深度拆解
1. 状态守护机制
2. 与状态模式的区别
二、生活化类比:自动售货机
三、Java代码实现(生产级Demo)
1. 完整可运行代码
2. 关键实现技术
四、横向对比表格
1. 相似模式对比
2. 线程安全方案选择
五、高级应用技巧
1. 组合模式增强
2. 日志增强实现
3. Spring应用场景
六、Balking模式变体与扩展(续)
1. 分布式场景下的Balking
2. 分级Balking策略
七、性能优化与陷阱规避
1. 状态检查的性能优化
2. 常见陷阱与解决方案
八、工业级应用案例
1. Tomcat连接器中的Balking
2. 电商库存扣减场景
九、与其他模式的组合应用
1. Balking + Observer 模式
2. Balking + Chain of Responsibility
十、终极对比表格:Balking模式家族
┌───────────────┐ ┌───────────────┐
│ Client │──────>│ GuardedObj │
└───────────────┘ ├───────────────┤
│ - state │
│ - checkState()│
│ - changeState()
└───────────────┘
系统组件 |
现实类比 |
核心行为 |
Client |
顾客投币 |
发起购买请求 |
checkState |
货品检测系统 |
检查库存和金额是否充足 |
Balking |
退币机制 |
条件不满足时立即退币 |
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();
}
}
// 1. 原子状态标记
private final AtomicBoolean changed = new AtomicBoolean(false);
// 2. 状态检查与重置一体化操作
changed.getAndSet(false)
// 3. 线程安全计数器
autoSaveCount.incrementAndGet()
模式 |
核心策略 |
适用场景 |
Balking |
条件不满足立即放弃 |
状态校验场景(如自动保存) |
Retry |
条件不满足循环重试 |
网络请求等可恢复场景 |
State |
委托给状态对象处理 |
复杂状态转换场景 |
Guard Suspension |
等待条件满足 |
必须完成的阻塞任务 |
实现方式 |
优点 |
缺点 |
synchronized |
简单可靠 |
性能开销较大 |
AtomicXXX |
无锁高性能 |
只适用于简单状态 |
ReentrantLock |
可中断/超时 |
需手动释放锁 |
volatile |
轻量级可见性保证 |
不保证复合操作原子性 |
// 结合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();
}
// 记录Balking事件
if (!changed.get()) {
auditLogger.log("Balking at " + LocalDateTime.now());
return;
}
@Component
public class ConfigMonitor {
@Scheduled(fixedRate = 5000)
public void reloadConfig() {
if (!GlobalConfig.isDirty()) {
return; // Balking
}
// 重新加载配置...
}
}
好的!我将延续原有结构,从 第六部分 开始扩展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;
}
}
关键点:
SETNX
命令替代本地原子变量// 根据业务重要性实现分级放弃
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();
}
// 执行处理...
}
}
优化手段 |
实现方式 |
适用场景 |
双重检查锁 |
先非阻塞检查,再同步块内二次检查 |
高并发读场景 |
状态标记分组 |
对不同资源分桶标记 |
多资源竞争场景 |
延迟状态重置 |
处理完成后再重置状态(减少CAS竞争) |
短时高频状态变更 |
// 双重检查锁实现示例
public class DoubleCheckBalking {
private volatile boolean busy = false;
public void execute() {
if (!busy) { // 第一次非阻塞检查
synchronized (this) {
if (!busy) { // 第二次原子检查
busy = true;
// 执行任务...
busy = false;
}
}
}
}
}
陷阱现象 |
根本原因 |
解决方案 |
活锁(Livelock) |
多个线程持续检查-放弃循环 |
引入随机退避时间 |
状态逃逸 |
对象引用被外部修改 |
防御性拷贝(Deep Copy) |
监控缺失 |
无法追踪放弃操作次数 |
添加Metrics计数器 |
// org.apache.tomcat.util.net.AbstractEndpoint
public boolean processSocket(SocketWrapperBase socket) {
if (running && !paused) {
// 将socket交给线程池处理
return executor.execute(new SocketProcessor(socket));
}
// 服务未运行立即放弃
return false;
}
设计启示:
running
和paused
双状态判断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);
}
}
public class ConfigMonitor {
private final List listeners = new CopyOnWriteArrayList<>();
private volatile String currentConfig;
// 配置变更通知(Balking条件检查)
public void updateConfig(String newConfig) {
if (Objects.equals(currentConfig, newConfig)) {
return; // 配置未变化时放弃
}
this.currentConfig = newConfig;
notifyListeners();
}
}
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);
}
变体名称 |
核心差异点 |
典型应用场景 |
Java SDK中的体现 |
经典Balking |
基于本地原子变量 |
单机资源控制 |
AtomicBoolean.getAndSet |
分布式Balking |
依赖外部存储状态 |
跨服务协调 |
Redis SETNX |
分级Balking |
按优先级差异化处理 |
业务流量分级 |
ThreadPoolExecutor拒绝策略 |
延迟Balking |
超时后才放弃 |
弱依赖服务调用 |
Future.get(timeout) |
批量Balking |
累积多个请求后统一判断 |
批量处理系统 |
BufferedWriter.flush |