解决Awaitility异步测试高内存问题,寻求低内存替代方案
寻找Awaitility的低内存替代方案
可行的低内存替代方案
1. 优化Awaitility的线程复用(最小改动方案)
Awaitility默认会为每个等待逻辑创建新线程,导致大规模测试时内存飙升。但可以通过配置全局复用的自定义线程池,避免线程频繁创建销毁的内存开销:
// 全局初始化可复用的线程池,根据测试并发量调整核心线程数 private static final ScheduledExecutorService CUSTOM_SCHEDULER = Executors.newScheduledThreadPool(4); static void optimizedWaitWithAwaitility() { AtomicInteger primeCount = new AtomicInteger(0); await() .with().scheduler(CUSTOM_SCHEDULER) // 指定自定义线程池 .atMost(1, TimeUnit.HOURS) .pollInterval(3, TimeUnit.SECONDS) .until(() -> { int number = supplierMethod().get(); if (isPrime(number)) { System.out.println("Found prime: " + number); primeCount.incrementAndGet(); } return primeCount.get() >= 500; }); } // 测试套件结束后关闭线程池,避免资源泄漏 @AfterClass public static void cleanupScheduler() { CUSTOM_SCHEDULER.shutdown(); }
该方案完全保留Awaitility的API使用习惯,仅需添加线程池配置,即可大幅降低内存占用。
2. 基于Java原生API实现轻量轮询(无第三方依赖)
如果想彻底摆脱第三方库的内存开销,可以用Java原生的ScheduledExecutorService+CountDownLatch实现自定义等待逻辑,完全控制线程数量:
static void customWaitWithNativeJava() throws InterruptedException { AtomicInteger primeCount = new AtomicInteger(0); CountDownLatch latch = new CountDownLatch(1); // 使用单线程调度器,内存占用最低 ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); Runnable pollTask = () -> { int number = supplierMethod().get(); if (isPrime(number)) { System.out.println("Found prime: " + number); if (primeCount.incrementAndGet() >= 500) { latch.countDown(); scheduler.shutdown(); // 条件满足后立即停止调度 } } }; // 立即执行第一次轮询,之后每隔3秒重复 scheduler.scheduleAtFixedRate(pollTask, 0, 3, TimeUnit.SECONDS); // 等待超时或条件触发 if (!latch.await(1, TimeUnit.HOURS)) { scheduler.shutdownNow(); System.err.println("Timeout exceeded"); } }
该方案无任何第三方依赖,线程数固定为1(可根据需求调整),内存开销极低,适合对资源占用敏感的大规模测试场景。
3. 优化响应式方案的内存占用
你之前的响应式实现内存过高,核心问题在于使用了Schedulers.boundedElastic()(会动态创建大量线程)以及冗余的订阅逻辑。优化后的实现如下:
public Mono<Integer> optimizedReactiveAwait( Supplier<Integer> supplierMethod, Duration pollInterval, Duration timeout ) { AtomicInteger primeCount = new AtomicInteger(0); return Mono.fromRunnable(() -> { int number = supplierMethod().get(); if (isPrime(number)) { System.out.println("Found prime: " + number); primeCount.incrementAndGet(); } }) .repeat(() -> primeCount.get() < 500) // 直接基于条件重复 .delayElement(pollInterval, Schedulers.single()) // 使用单线程调度器 .last() .timeout(timeout) .onErrorResume(e -> { System.err.println("Timeout exceeded"); return Mono.empty(); }); } static void waitWithOptimizedReactive() { Mono<Integer> result = new DifferentAwaitility().optimizedReactiveAwait( () -> supplierMethod().get(), Duration.ofSeconds(3), Duration.ofHours(1) ); Integer finalResult = result.block(); if (finalResult != null) { System.out.println("Result: " + finalResult); } else { System.err.println("Error: Timeout or other failure occurred"); } }
通过使用Schedulers.single()复用单线程、简化重复逻辑,可将响应式方案的内存占用降至与优化后的Awaitility相当的水平。
方案选型建议
- 若想保留Awaitility的使用习惯:优先选择优化Awaitility线程复用的方案,改动最小,见效最快
- 若追求极致内存效率:选择Java原生API实现,无依赖,资源占用完全可控
- 若项目已基于响应式栈:使用优化后的响应式方案,适配现有技术栈
内容的提问来源于stack exchange,提问作者Sal-laS
相关产品推荐
相关产品推荐

