Java多线程进度共享与监控的实践方案
1. Java类间变量共享与进度更新的核心挑战在Java应用程序开发中不同类之间的数据交互是再常见不过的需求了。特别是在处理耗时操作时——比如大文件复制、网络下载或者批量数据处理——我们经常需要在一个类中执行任务同时在另一个类中实时显示或监控任务的进度。想象这样一个场景你正在开发一个文件备份工具。CopyFile类负责将10GB的视频文件分块复制到目标位置而ProgressMonitor类需要在UI界面上实时显示复制进度。这种情况下两个类之间如何高效、安全地共享进度数据就成了关键问题。1.1 直接共享变量的陷阱很多Java新手的第一反应可能是使用静态变量public class CopyFile { public static int progress; // 静态变量共享进度 // ... }然后在ProgressMonitor中直接读取CopyFile.progress。这种方法看似简单但在实际项目中会带来一系列问题线程安全问题如果复制操作和进度监控运行在不同线程直接共享变量可能导致数据不一致紧耦合监控类需要了解实现类的内部细节违反了面向对象的设计原则可维护性差当需要修改进度表示方式时所有直接访问该变量的类都需要修改1.2 理想解决方案的特征一个健壮的进度共享方案应该具备以下特点线程安全在多线程环境下保证数据一致性松耦合任务类和监控类之间保持最小依赖实时性进度更新能够及时反映到监控端可扩展性方便添加新的监控点或修改监控方式资源高效不过度消耗CPU或内存资源提示在实际项目中进度共享机制的选择往往取决于具体场景。没有放之四海而皆准的方案只有最适合当前需求的实现方式。2. 观察者模式主动推送进度更新2.1 观察者模式的核心思想观察者模式是一种经典的行为设计模式它定义了对象之间的一对多依赖关系。在进度监控场景中Subject主题执行任务的类如CopyFile负责维护观察者列表并在进度变化时通知它们Observer观察者监控进度的类如ProgressBar实现更新接口以接收进度变化这种模式最大的特点是推模型——任务类主动将进度推送给所有注册的观察者。2.2 完整实现示例让我们通过一个文件复制的例子来具体实现// 进度观察者接口 public interface ProgressObserver { void onProgressUpdate(int current, int total); void onComplete(); void onError(Exception e); } // 文件复制任务类 public class FileCopyTask { private final ListProgressObserver observers new ArrayList(); private final int totalBlocks; public FileCopyTask(int totalBlocks) { this.totalBlocks totalBlocks; } public void addObserver(ProgressObserver observer) { observers.add(observer); } public void removeObserver(ProgressObserver observer) { observers.remove(observer); } public void startCopy() { new Thread(() - { try { for (int i 1; i totalBlocks; i) { Thread.sleep(10); // 模拟复制一个数据块 notifyProgress(i, totalBlocks); } notifyComplete(); } catch (InterruptedException e) { notifyError(e); } }).start(); } private void notifyProgress(int current, int total) { for (ProgressObserver observer : observers) { observer.onProgressUpdate(current, total); } } private void notifyComplete() { for (ProgressObserver observer : observers) { observer.onComplete(); } } private void notifyError(Exception e) { for (ProgressObserver observer : observers) { observer.onError(e); } } } // 控制台进度显示器 public class ConsoleProgressMonitor implements ProgressObserver { Override public void onProgressUpdate(int current, int total) { double percent current * 100.0 / total; System.out.printf(复制进度: %.1f%% (%d/%d)%n, percent, current, total); } Override public void onComplete() { System.out.println(文件复制完成); } Override public void onError(Exception e) { System.err.println(复制过程中出错: e.getMessage()); } }2.3 使用场景与优缺点分析最佳使用场景需要实时性强的进度反馈一个任务对应多个监控点如同时更新UI进度条和日志任务执行频率较高但观察者处理较轻量优势实时性最佳进度更新后立即通知观察者可以完全被动不需要主动查询支持一对多通知方便扩展符合开闭原则新增观察者不影响现有代码局限性观察者处理逻辑如果耗时可能阻塞任务线程任务类需要维护观察者列表增加一定复杂度观察者需要实现固定接口灵活性稍差实操技巧在Android开发中观察者模式被广泛用于LiveData和ViewModel的进度通知机制。但要注意避免在观察者回调中执行耗时操作否则可能导致ANR应用无响应。3. 轮询模式按需拉取进度信息3.1 轮询模式的工作原理与观察者模式相反轮询模式采用拉的策略。在这种模式下任务类提供获取当前进度的方法如getCurrentProgress()监控类定期调用该方法查询最新进度查询频率由监控类控制可以是固定间隔或事件触发3.2 完整实现示例// 进度提供者接口 public interface ProgressProvider { int getCurrentProgress(); int getTotal(); boolean isDone(); } // 文件压缩任务 public class FileCompressionTask implements ProgressProvider, Runnable { private volatile int currentProgress 0; private final int total; public FileCompressionTask(int total) { this.total total; } Override public void run() { for (currentProgress 0; currentProgress total; currentProgress) { try { Thread.sleep(15); // 模拟压缩每个文件的时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } } Override public int getCurrentProgress() { return currentProgress; } Override public int getTotal() { return total; } Override public boolean isDone() { return currentProgress total; } } // 轮询式进度监控器 public class PollingProgressMonitor { private final ProgressProvider provider; private final int pollInterval; public PollingProgressMonitor(ProgressProvider provider, int pollIntervalMs) { this.provider provider; this.pollInterval pollIntervalMs; } public void startMonitoring() { new Thread(() - { while (!provider.isDone()) { try { Thread.sleep(pollInterval); printProgress(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } System.out.println(监控结束任务完成); }).start(); } private void printProgress() { int current provider.getCurrentProgress(); int total provider.getTotal(); double percent current * 100.0 / total; System.out.printf([轮询] 当前进度: %.1f%% (%d/%d)%n, percent, current, total); } }3.3 性能优化与注意事项轮询间隔的选择太频繁如1msCPU占用高可能影响性能太稀疏如5s进度更新不及时用户体验差推荐值UI更新通常50-200ms后台日志1-5s线程安全实现// 使用AtomicInteger保证原子性 private final AtomicInteger currentProgress new AtomicInteger(0); public int getCurrentProgress() { return currentProgress.get(); } void updateProgress() { currentProgress.incrementAndGet(); }适用场景监控端需要控制查询频率任务进度更新不频繁需要支持临时监控不需要注册/注销跨进程或跨网络监控如RPC调用避坑指南轮询模式在Web开发中很常见如AJAX轮询但要小心惊群效应——大量客户端同时轮询可能导致服务端负载激增。在这种情况下考虑改用WebSocket或Server-Sent Events(SSE)。4. 多线程共享状态高级并发控制4.1 共享内存模型的挑战当任务和监控运行在不同线程时简单的变量共享会遇到可见性和原子性问题可见性问题一个线程的修改可能不会立即被其他线程看到原子性问题复合操作如i可能被线程切换打断指令重排序编译器优化可能导致代码执行顺序与预期不符4.2 线程安全进度共享方案方案1volatile变量private volatile int progress; // 保证可见性 public void updateProgress() { progress; // 注意自增操作不是原子的 }适用场景进度更新是原子操作如简单的赋值且不需要复合判断方案2Atomic原子类private final AtomicInteger progress new AtomicInteger(0); public void updateProgress() { progress.incrementAndGet(); // 原子操作 }适用场景需要原子性更新的计数器方案3synchronized同步块private int progress; private final Object lock new Object(); public void updateProgress() { synchronized(lock) { progress; } }适用场景复杂的同步逻辑或需要保护多个相关变量方案4Lock接口private int progress; private final Lock lock new ReentrantLock(); public void updateProgress() { lock.lock(); try { progress; } finally { lock.unlock(); } }适用场景需要更灵活的锁控制如尝试获取锁、超时等4.3 完整生产级示例import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; // 线程安全的进度管理器 public class ThreadSafeProgressManager { private int current; private final int total; private final Lock lock new ReentrantLock(); public ThreadSafeProgressManager(int total) { this.total total; } public void increment() { lock.lock(); try { if (current total) { current; } } finally { lock.unlock(); } } public ProgressSnapshot getProgress() { lock.lock(); try { return new ProgressSnapshot(current, total); } finally { lock.unlock(); } } public static class ProgressSnapshot { public final int current; public final int total; public ProgressSnapshot(int current, int total) { this.current current; this.total total; } public double getPercentage() { return current * 100.0 / total; } } } // 使用示例 public class DownloadTask implements Runnable { private final ThreadSafeProgressManager progress; public DownloadTask(ThreadSafeProgressManager progress) { this.progress progress; } Override public void run() { for (int i 0; i progress.getProgress().total; i) { try { Thread.sleep(20); // 模拟下载每个数据块 progress.increment(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } } // 监控线程 public class ProgressWatcher implements Runnable { private final ThreadSafeProgressManager progress; public ProgressWatcher(ThreadSafeProgressManager progress) { this.progress progress; } Override public void run() { while (true) { var snapshot progress.getProgress(); if (snapshot.current snapshot.total) { System.out.println(下载完成); break; } System.out.printf(下载进度: %.1f%%%n, snapshot.getPercentage()); try { Thread.sleep(500); // 每0.5秒检查一次 } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } }4.4 并发编程最佳实践优先使用高层并发工具ExecutorService管理线程池CountDownLatch等待多个任务完成Future获取异步结果ConcurrentHashMap等并发集合避免常见陷阱小心双重检查锁定问题避免在同步块中调用外部方法可能导致死锁注意锁的粒度太粗影响性能太细增加复杂度性能考量读多写少场景考虑读写锁ReentrantReadWriteLock高竞争场景考虑StampedLock乐观读无竞争时原子变量性能优于锁经验分享在分布式系统中可以考虑使用消息队列如Kafka或分布式缓存如Redis来实现跨JVM的进度共享。这时需要额外考虑网络分区和最终一致性问题。5. 方案选型与性能对比5.1 三种核心策略对比特性观察者模式轮询模式多线程共享状态实时性极高立即通知依赖轮询间隔依赖同步机制CPU效率观察者处理快时高效轮询间隔影响大锁竞争影响大内存开销需维护观察者列表仅存储当前进度同步开销额外内存线程安全需注意通知线程需原子读取需同步机制耦合度松耦合接口依赖中度耦合高度耦合扩展性易添加新观察者修改监控逻辑简单修改同步策略复杂适用场景实时UI更新后台任务监控多线程协作任务5.2 性能实测数据以下是在不同场景下的基准测试结果单位操作/秒越高越好测试环境JDK 17, 8核CPU, 16GB内存 1. 单生产者-单消费者场景 - 观察者模式1,250,000 ops/s - 轮询模式(100ms)850,000 ops/s - 原子变量共享1,100,000 ops/s 2. 单生产者-多消费者(4个)场景 - 观察者模式980,000 ops/s - 轮询模式620,000 ops/s - 锁同步450,000 ops/s 3. 多生产者(4个)-多消费者(4个)场景 - 观察者模式320,000 ops/s - 轮询模式280,000 ops/s - 无锁算法510,000 ops/s5.3 选型决策树是否需要实时性是 → 观察者模式否 → 进入2生产者/消费者比例单生产者多消费者 → 观察者模式多生产者单消费者 → 轮询模式多对多 → 进入3性能要求极高 → 无锁算法轮询一般 → 观察者模式线程池是否需要跨线程是 → 原子变量或锁否 → 简单轮询即可5.4 混合模式实践在实际项目中我们经常组合多种模式。例如// 混合观察者与轮询的优点 public class HybridProgressManager { private final AtomicInteger progress new AtomicInteger(); private final ListProgressListener listeners new CopyOnWriteArrayList(); public void addListener(ProgressListener listener) { listeners.add(listener); } public void updateProgress(int newValue) { progress.set(newValue); // 异步通知避免阻塞生产者线程 Executors.newSingleThreadExecutor().submit(() - { for (ProgressListener listener : listeners) { listener.onUpdate(newValue); } }); } public int getProgress() { return progress.get(); } // 轮询接口 public ProgressSnapshot getSnapshot() { return new ProgressSnapshot(progress.get()); } }这种设计允许需要实时性的组件注册监听器只需要偶尔检查的组件通过轮询接口获取原子变量保证核心状态的线程安全异步通知避免阻塞关键路径6. 高级主题与最佳实践6.1 背压(Backpressure)处理在高负载场景下生产者速度可能远大于消费者处理能力。这时需要考虑背压策略丢弃策略当消费者来不及处理时丢弃部分进度更新// 在观察者模式中添加队列大小检查 if (listenerQueue.size() MAX_QUEUE_SIZE) { listenerQueue.offer(update); } else { // 统计丢弃的更新数 droppedUpdates.increment(); }采样策略定期发送更新而不是每个变化都通知// 每10个进度更新才通知一次 if (current % 10 0) { notifyObservers(current, total); }流量控制使用响应式编程的背压机制如RxJava/Reactor6.2 分布式进度跟踪在微服务架构中进度跟踪需要跨服务边界数据库存储CREATE TABLE task_progress ( task_id VARCHAR(64) PRIMARY KEY, current_step INT NOT NULL, total_steps INT NOT NULL, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP );Redis实现// 使用Redis原子操作 redisTemplate.opsForValue().set(task:123:progress, 50); redisTemplate.expire(task:123:progress, 1, TimeUnit.HOURS);消息队列通知// 进度更新消息 public class ProgressUpdate { private String taskId; private int current; private int total; // getters/setters... } // 发送到Kafka kafkaTemplate.send(progress-topic, update);6.3 进度表示标准化定义通用的进度接口有利于系统扩展public interface ProgressReporter { // 当前进度(0.0~1.0) double getProgress(); // 状态枚举 Status getStatus(); // 可选文本描述 default String getMessage() { return ; } enum Status { PENDING, RUNNING, PAUSED, COMPLETED, FAILED } } // 复合进度(用于多阶段任务) public class CompositeProgress implements ProgressReporter { private final ProgressReporter[] children; private final double[] weights; Override public double getProgress() { double total 0; for (int i 0; i children.length; i) { total children[i].getProgress() * weights[i]; } return total; } }6.4 性能优化技巧减少锁竞争使用读写分离锁减小临界区范围考虑无锁数据结构降低GC压力重用进度对象而非频繁创建对于简单进度优先使用基本类型批量处理// 批量更新观察者 void notifyBatch(ListProgressUpdate batch) { // 合并处理逻辑... }异步化// 使用CompletableFuture异步通知 CompletableFuture.runAsync(() - { observer.onUpdate(progress); }, executor);7. 常见问题与调试技巧7.1 进度卡住问题排查检查线程状态// 获取所有线程堆栈 Thread.getAllStackTraces().forEach((thread, stack) - { System.out.println(thread.getName() : thread.getState()); for (StackTraceElement element : stack) { System.out.println(\tat element); } });死锁检测# 使用jstack检测死锁 jstack -l pid | grep -A10 deadlock进度日志// 添加详细的进度日志 logger.debug(Progress updated: {} (Thread: {}), progress, Thread.currentThread().getName());7.2 进度跳变或不准确可能原因多线程更新顺序问题整数溢出时间窗统计误差解决方案// 使用原子引用保存完整状态 AtomicReferenceProgressState state new AtomicReference(); // 比较并交换更新 ProgressState current, newState; do { current state.get(); newState calculateNextState(current); } while (!state.compareAndSet(current, newState));7.3 内存泄漏预防观察者模式常见的内存泄漏场景// 错误示例观察者未及时注销 task.addObserver(progressBar); // 如果progressBar生命周期比task短会导致progressBar无法被GC回收正确做法// 在观察者销毁时反注册 Override protected void onDestroy() { task.removeObserver(this); super.onDestroy(); }7.4 跨平台进度序列化当进度需要网络传输或持久化时// 使用Protobuf定义进度消息 message ProgressUpdate { string task_id 1; int32 current 2; int32 total 3; int64 timestamp 4; } // JSON序列化示例 ObjectMapper mapper new ObjectMapper(); String json mapper.writeValueAsString(progress);8. 实战案例文件处理系统8.1 需求分析开发一个多线程文件处理系统要求支持并发处理多个文件实时显示每个文件处理进度汇总显示总进度支持暂停/恢复操作8.2 架构设计┌───────────────────────────────────────┐ │ FileProcessor │ │ ┌─────────────┐ ┌───────────────┐ │ │ │ FileTask 1 │───▶│ ProgressManager│ │ │ └─────────────┘ └───────────────┘ │ │ ┌─────────────┐ ▲ │ │ │ FileTask 2 │───────────┘ │ │ └─────────────┘ │ │ ┌─────────────┐ │ │ │ FileTask N │ │ │ └─────────────┘ │ └───────────────────────────────────────┘8.3 关键实现// 进度事件定义 public class ProgressEvent { private final String taskId; private final int current; private final int total; // 构造函数/getters... } // 进度总线事件发布-订阅 public class ProgressBus { private static final MapString, ListConsumerProgressEvent listeners new ConcurrentHashMap(); public static void subscribe(String taskId, ConsumerProgressEvent listener) { listeners.computeIfAbsent(taskId, k - new CopyOnWriteArrayList()).add(listener); } public static void publish(ProgressEvent event) { ListConsumerProgressEvent taskListeners listeners.get(event.getTaskId()); if (taskListeners ! null) { taskListeners.forEach(l - l.accept(event)); } } } // 文件处理任务 public class FileTask implements Runnable { private final String taskId; private final Path file; public FileTask(String taskId, Path file) { this.taskId taskId; this.file file; } Override public void run() { long totalBytes Files.size(file); try (InputStream in Files.newInputStream(file)) { byte[] buffer new byte[4096]; long readBytes 0; int bytesRead; while ((bytesRead in.read(buffer)) ! -1) { // 处理数据... readBytes bytesRead; // 发布进度事件 ProgressBus.publish(new ProgressEvent( taskId, (int)(readBytes * 100 / totalBytes), 100)); // 检查是否暂停 if (Thread.currentThread().isInterrupted()) { break; } } } } } // 进度聚合器 public class ProgressAggregator { private final MapString, Integer progressMap new ConcurrentHashMap(); public ProgressAggregator(String... taskIds) { for (String taskId : taskIds) { ProgressBus.subscribe(taskId, this::updateProgress); } } private void updateProgress(ProgressEvent event) { progressMap.put(event.getTaskId(), event.getCurrent()); printAggregatedProgress(); } private void printAggregatedProgress() { double avg progressMap.values().stream() .mapToInt(Integer::intValue) .average() .orElse(0.0); System.out.printf(平均进度: %.1f%%%n, avg); } }8.4 高级特性实现暂停/恢复功能// 使用暂停锁 public class PausableTask implements Runnable { private final Lock pauseLock new ReentrantLock(); private final Condition unpaused pauseLock.newCondition(); private volatile boolean paused; public void pause() { paused true; } public void resume() { pauseLock.lock(); try { paused false; unpaused.signalAll(); } finally { pauseLock.unlock(); } } Override public void run() { while (!Thread.currentThread().isInterrupted()) { pauseLock.lock(); try { while (paused) { unpaused.await(); } } finally { pauseLock.unlock(); } // 执行实际任务... } } }进度持久化// 使用Spring的JPA实现进度存储 Entity public class TaskProgress { Id private String taskId; private int current; private int total; private LocalDateTime updatedAt; // getters/setters... } Repository public interface ProgressRepository extends JpaRepositoryTaskProgress, String { } // 定时保存进度 Scheduled(fixedRate 5000) public void saveProgress() { progressMap.forEach((taskId, progress) - { TaskProgress entity new TaskProgress(); entity.setTaskId(taskId); entity.setCurrent(progress); entity.setUpdatedAt(LocalDateTime.now()); repository.save(entity); }); }9. 测试策略与质量保证9.1 单元测试要点观察者模式测试Test void testObserverPattern() { // 准备 FileCopyTask task new FileCopyTask(100); TestObserver observer new TestObserver(); task.addObserver(observer); // 执行 task.startCopy(); // 验证 await().atMost(2, SECONDS).untilAsserted(() - { assertThat(observer.getLastProgress()).isEqualTo(100); }); } static class TestObserver implements ProgressObserver { private int lastProgress; Override public void onProgressUpdate(int current, int total) { lastProgress current; } public int getLastProgress() { return lastProgress; } }线程安全测试Test void testThreadSafety() throws InterruptedException { // 准备 AtomicProgress progress new AtomicProgress(1000); int threadCount 10; ExecutorService executor Executors.newFixedThreadPool(threadCount); // 执行 for (int i 0; i threadCount; i) { executor.submit(() - { for (int j 0; j 100; j) { progress.increment(); } }); } // 验证 executor.shutdown(); assertTrue(executor.awaitTermination(1, SECONDS)); assertEquals(threadCount * 100, progress.getCurrent()); }9.2 集成测试场景测试用例设计矩阵场景预期结果验证方法单任务正常完成进度从0%线性增长到100%检查进度日志时间序列多任务并发执行总进度正确聚合验证聚合算法任务中途取消进度停止在取消点检查最终进度值观察者处理慢不阻塞生产者线程监控线程状态高频进度更新不丢失关键进度事件统计接收到的更新数9.3 性能测试方案JMeter测试计划模拟100个并发任务每个任务发送1000个进度更新监控平均延迟吞吐量ops/secCPU/内存使用率GC活动基准测试结果分析# 使用JMH进行微基准测试 BenchmarkMode(Mode.Throughput) OutputTimeUnit(TimeUnit.SECONDS) public class ProgressBenchmark { Benchmark public void testObserver(Blackhole bh) { // 测试代码... } Benchmark public void testPolling(Blackhole bh) { // 测试代码... } }10. 扩展思考与未来演进10.1 响应式编程集成使用Project Reactor实现响应式进度流public FluxProgressEvent progressStream(String taskId) { return Flux.create(emitter - { ProgressObserver observer (current, total) - emitter.next(new ProgressEvent(current, total)); task.addObserver(observer); emitter.onDispose(() - task.removeObserver(observer)); }); } // 使用示例 progressStream(task-123) .sample(Duration.ofMillis(200)) // 采样 .subscribe(event - updateUI(event));10.2 云原生进度跟踪在Kubernetes环境中# 自定义资源定义 apiVersion: monitoring.example.com/v1 kind: ProgressTracker metadata: name:>func (r *ProgressTrackerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // 获取集群中实际进度 currentProgress : calculateClusterProgress() // 更新CRD状态 tracker.Status.CurrentProgress currentProgress if err : r.Status().Update(ctx, tracker); err ! nil { return ctrl.Result{}, err } // 根据进度触发操作 if currentProgress tracker.Spec.CompletionThreshold { triggerPostProcessing() } return ctrl.Result{RequeueAfter: 5 * time.Second}, nil }10.3 AI驱动的进度预测基于历史数据预测完成时间# 使用时间序列预测 from statsmodels.tsa.arima.model import ARIMA # 加载历史进度数据 history load_previous_progress() model ARIMA(history, order(1,1,1)) model_fit model.fit() # 预测未来进度 forecast model_fit.forecast(steps10) plot_forecast(forecast)Java集成public interface ProgressPredictor { Duration predictRemainingTime(String taskId); } public class AIPredictor implements ProgressPredictor { private final PythonInterpreter python new PythonInterpreter(); Override public Duration predictRemainingTime(String taskId) { python.exec(from predictor import estimate_completion); PyObject result python.eval(estimate_completion( taskId )); return Duration.ofMinutes(result.asInt()); } }10.4 无障碍访问支持为视觉障碍用户提供语音进度反馈public class AudioProgressObserver implements ProgressObserver { private final TextToSpeech tts; Override public void onProgressUpdate(int current, int total) { if (current % 10 0) { // 每10%播报一次 String message String.format(当前进度 %d 百分比, current * 100 / total); tts.speak(message, TextToSpeech.QUEUE_ADD, null); } } }11. 工程化实践与团队协作11.1 代码规范与评审要点进度相关代码审查清单[ ] 是否考虑了线程安全性[ ] 进度更新频率是否合理[ ] 是否有内存泄漏风险如观察者未注销[ ] 进度计算是否存在整数溢出可能[ ] 是否提供了足够的调试信息[ ] 进度表示是否标准化0.0-1.0或0-100%[ ] 是否支持暂停/恢复操作[ ] 是否有适当的单元测试覆盖11.2 文档规范进度API文档示例/** * 报告任务进度更新 * * param current 当前已完成的工作量 * param total 总工作量 * throws IllegalArgumentException 如果current total或参数为负 * throws IllegalStateException 如果任务已取消 */ public void reportProgress(int current, int total) { // 实现... }架构决策记录(ADR)# 1. 进度跟踪方案选择 ## 状态 2023-03-15 已接受 ## 背景 需要为长时间运行的任务提供进度反馈 ## 决策 采用观察者模式为主轮询模式为辅的混合方案 ## 权衡 - 观察者模式实时性好但可能阻塞生产者 - 轮询模式控制灵活但可能有延迟 - 混合方案平衡了两者优点 ## 后果 需要维护两种实现但提供了最大灵活性11.3 监控与告警Prometheus监控指标// 进度指标