You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JMH基准测试Java方法中特定的future.get()循环代码段

用JMH测试future.get()循环的可行方案

你这个需求是可以实现的,核心是借助JMH的状态管理和生命周期注解,把前置准备逻辑和要测试的future.get()循环拆分,让准备工作在基准测试启动前完成,同时规避JMH进程隔离的限制。

具体实现思路

  • 用@State(Scope.Benchmark)定义状态类,存放ScheduledExecutorService、Collection<Future>、测试文件等资源,JMH会在基准测试进程中自动管理这个状态实例。
  • 用@Setup(Level.Trial)标记初始化方法,在整个基准测试批次启动前仅执行一次:创建测试文件、提交所有异步任务并收集Future对象。
  • 基准测试方法只保留future.get()的循环逻辑,直接从状态类中获取预先生成的futures集合。
  • 用@TearDown(Level.Trial)标记清理方法,测试结束后关闭线程池、删除测试文件,避免资源残留。

完整代码示例

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Collection;
import java.util.HashSet;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@State(Scope.Benchmark)
public class FutureGetBenchmark {

    private ScheduledExecutorService exec;
    private Collection<Future<?>> futures;
    private File testFile;

    @Setup(Level.Trial)
    public void setup() throws IOException {
        // 初始化线程池
        exec = new ScheduledThreadPoolExecutor(50);
        testFile = new File("./test");
        testFile.createNewFile();
        futures = new HashSet<>();

        // 提交所有异步任务并收集Future
        for (int i = 0; i < 50; i++) {
            final int j = i;
            Future<?> future = exec.submit(() -> {
                try {
                    Files.write(testFile.toPath(), ("Test-" + j).getBytes(StandardCharsets.UTF_8));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            futures.add(future);
        }
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MILLISECONDS)
    public void benchmarkFutureGet() throws Exception {
        // 仅测试这段future.get()循环逻辑
        for (Future<?> future : futures) {
            future.get();
        }
    }

    @TearDown(Level.Trial)
    public void tearDown() throws IOException {
        // 清理资源
        exec.shutdown();
        if (testFile.exists()) {
            testFile.delete();
        }
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(FutureGetBenchmark.class.getSimpleName())
                .forks(1)
                .warmupIterations(3)
                .measurementIterations(5)
                .build();
        new Runner(options).run();
    }
}

关键注意事项

  • @Setup(Level.Trial)保证初始化逻辑只执行一次,避免每次基准测试迭代都重复提交任务,确保测试的是纯粹等待Future完成的耗时。
  • 虽然方案可行,但文件IO的不确定性(磁盘性能、系统缓存等)会干扰测试结果的稳定性。如果只是替代System.currentTimeMillis测量等待耗时,建议把异步任务中的IO换成无副作用的计算(比如简单数值运算),这样结果更能反映future.get()本身的性能。
  • JMH默认会fork新进程执行测试,状态类实例会在fork后的进程中初始化,完全适配JMH的运行机制。

内容的提问来源于stack exchange,提问作者PentaKon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:20:13