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

Java多线程更新Mellanox固件时出现重复操作同一设备问题

问题核心原因

你的代码中ProcessBuilder实例(也就是代码里的builder变量)是多线程共享的非线程安全对象,这是导致参数被覆盖的直接原因:

  • 所有线程在gate.await()之前都会调用builder.command()修改同一个实例的命令参数
  • CyclicBarrier会等所有线程都执行到gate.await()这行才一起放行,此时所有线程已经完成了builder.command()的调用,最后一轮循环的线程设置的参数会把前面所有线程的设置全部覆盖
  • 所以当所有线程同时执行startProcess()时,调用的都是最后一个网卡、最后一个IP对应的命令,自然会出现4个线程同时操作同一台设备的情况,第一个抢占成功的线程执行完成,剩下3个因为设备被占用抛出报错,刚好和你遇到的现象完全匹配。

其他潜在问题

  1. streamGobbler看起来也是全局共享的实例,多线程同时读取进程输出会导致结果混乱,甚至出现死锁、输出丢失的问题
  2. 你的异常捕获逻辑里没有调用latch.countDown(),如果某个线程执行报错,latch.await()会永远阻塞无法继续执行

修复方案

  1. 每个线程内部创建独立的ProcessBuilder实例,不要共享全局实例,彻底避免参数被覆盖
  2. 每个线程配套创建独立的streamGobbler实例处理对应进程的输出
  3. 异常捕获块中补充latch.countDown()调用,避免程序永久阻塞

修复后代码示例

final CyclicBarrier gate = new CyclicBarrier(mellanox.size()+1);
ArrayList<Thread> threads = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(mellanox.size());
for (int index = 0; index < mellanox.size(); index++) {
    String mellanoxCard = mellanox.get(index);
    String ip = ipAddresses.get(index);
    Thread t = new Thread(() -> {
        try {
            if (isWindows) {
                // 每个线程创建独立的ProcessBuilder,不要用全局的builder
                ProcessBuilder localBuilder = new ProcessBuilder();
                localBuilder.command(flint, "-d", mellanoxCard, "-i", mellanoxFWPath, "-y", "burn");
                setupLoading.setProgressInfo("Updating Mellanox FW for System " + ip, "calibri");
                gate.await();
                // 这里传入localBuilder启动进程,或者修改startProcess方法接收ProcessBuilder作为参数
                Process process = localBuilder.start();
                // 每个线程创建独立的streamGobbler处理当前进程的输出
                StreamGobbler localGobbler = new StreamGobbler(process.getInputStream());
                localGobbler.start();
                process.waitFor();
                latch.countDown();
                setupLoading.setProgressInfo(localGobbler.getResult(), "consolas");
            }
        } catch (InterruptedException | BrokenBarrierException | IOException e) {
            setupLoading.setProgressInfo("An error has occured during Mellanox Firmware update!", "warning");
            writeErrorLog(e.getMessage());
            // 异常场景也减少计数,避免latch永久等待
            latch.countDown();
        }
    });
    threads.add(t);
}
for(Thread t : threads) t.start();
try {
    gate.await();
    latch.await();
} catch (InterruptedException | BrokenBarrierException e) {
    writeErrorLog(e.getMessage());
}

补充验证方式

如果你暂时不想改代码,可以在startProcess()执行前加一行日志,打印当前builder.command()的实际值,就能确认所有线程执行时用的参数是不是都是同一个。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 08:48:02