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

如何通过多线程在控制台实现多行进度条(类似CentOS yum进度样式)

如何用Java实现多线程多行控制台进度条?

我已经编写了一个单线程单行的控制台进度条方法,效果和代码如下:

[>>>>>>>>>>>>>>>>>>>>=====================================]

public void progress(int percent){
    System.out.print('\r');
    String progressText = "[" + StringUtils.repeat(">", percent) + StringUtils.repeat("=", 100-percent) + "]";
    System.out.print(progressText);
}

现在我希望通过多线程实现多行进度条打印,预期效果如下:

Thread-1: [>>>>>>>>>>>>>>>>>>>>===]
Thread-2: [>>>>>>>>>>>>>>>>>>>>>>>
]

也就是类似CentOS中yum install xxx -y的多行进度展示样式,请问这个需求是否可行?


当然可行!核心思路是利用ANSI转义序列来精准控制控制台光标的位置,让每个线程能定位到自己专属的行去更新进度,不会和其他线程的输出互相干扰。

实现原理

单线程里的\r只能让光标回到当前行开头,但多线程需要更精细的光标控制:

  • \033[nA:让光标向上移动n行
  • \033[nB:让光标向下移动n行
  • \033[K:清除从光标到行尾的内容(可选,避免残留旧字符)

完整代码示例

下面是一个可直接运行的实现,每个线程负责维护自己的进度行:

import org.apache.commons.lang3.StringUtils;

public class MultiThreadProgressBar {
    // 用锁保证控制台操作的线程安全,避免光标混乱
    private static final Object CONSOLE_LOCK = new Object();
    private static int activeThreadLines = 0;
    private final String threadLabel;
    private int assignedLine;

    public MultiThreadProgressBar(String threadLabel) {
        this.threadLabel = threadLabel;
        synchronized (CONSOLE_LOCK) {
            // 给每个线程分配唯一行号,先打印初始的空进度条
            assignedLine = ++activeThreadLines;
            System.out.printf("%s: [==================================================]%n", threadLabel);
        }
    }

    public void updateProgress(int percent) {
        // 确保百分比在0-100的合法范围内
        percent = Math.max(0, Math.min(100, percent));
        synchronized (CONSOLE_LOCK) {
            // 移动光标到当前线程的行开头:向上移动(总线程数 - 当前行号)行,再回到行首
            System.out.printf("\033[%dA\r", activeThreadLines - assignedLine);
            // 构建进度条文本
            String progressBar = "[" + StringUtils.repeat(">", percent) + StringUtils.repeat("=", 100 - percent) + "]";
            // 打印更新后的进度行
            System.out.printf("%s: %s%n", threadLabel, progressBar);
            // 把光标移回最后一行,避免后续输出干扰其他进度条
            System.out.printf("\033[%dB", activeThreadLines - assignedLine);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 创建两个进度条实例
        MultiThreadProgressBar bar1 = new MultiThreadProgressBar("Thread-1");
        MultiThreadProgressBar bar2 = new MultiThreadProgressBar("Thread-2");

        // 模拟线程1的进度更新(较快)
        new Thread(() -> {
            for (int i = 0; i <= 100; i++) {
                bar1.updateProgress(i);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();

        // 模拟线程2的进度更新(较慢)
        new Thread(() -> {
            for (int i = 0; i <= 100; i++) {
                bar2.updateProgress(i);
                try {
                    Thread.sleep(70);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();
    }
}

关键注意事项

  • 线程安全:一定要用锁来包裹光标移动和打印操作,不然多线程同时操作控制台会导致光标乱飞、进度条错乱。
  • 终端兼容性:大部分现代终端(Linux/macOS终端、Windows Terminal/PowerShell)都支持ANSI转义序列,但旧版Windows CMD可能不支持,需要开启虚拟终端模式才能正常显示。
  • 排版一致性:保持每个进度行的字符长度一致,避免更新时出现行错位的情况。

这样就能实现类似yum的多行实时进度更新效果啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:56:25