如何通过多线程在控制台实现多行进度条(类似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
相关产品推荐
相关产品推荐

