Java|进度条工具
#Java [字体 小·中·大]有时候批量处理任务无法观察执行情况,例如处理进度、处理数量、耗时等,这种“不确定性”会隐藏 bug,等到出问题就晚了。今天,这篇文章的“主角” —— 进度条工具 —— 就是为了解决这个问题。
进度条工具输出预览:
1[4B645A43] 远程请求记录: [#### ] - 22.91% (9835/42920), 耗时: 2m32s
2 (1) (2) (3) (4) (5) (6)
3
4各部分说明:
5
6(1) Trace ID
7(2) 进度条名称
8(3) 进度条面板
9(4) 执行占比
10(5) 执行数量细节
11(6) 执行耗时
实现比较简单,源码如下:
1public class ProgressBar {
2 private final String name;
3 private int begin;
4 private final int end;
5 private final int step;
6 private final int printIntervalMills; // 打印间隔, 毫秒
7 private final long startMills;
8 private long lastPrintMills;
9 private final String traceId;
10 private final Logger log;
11 // "[%s] %s: %s - %s (%d/%d), 耗时: %s"
12 private static final String TPL = "[%s] %s: %s - %s (%d/%d), 耗时: %s";
13 private static final String PGS = "[####################][ ]";
14
15 /** 默认打印间隔 5s */
16 public static ProgressBar of(String name, int total) {
17 return new ProgressBar(name, total, 1, 5000, null);
18 }
19
20 public static ProgressBar of(String name, int total, int printIntervalMills) {
21 return new ProgressBar(name, total, 1, printIntervalMills, null);
22 }
23
24 /** 默认打印间隔 5s */
25 public static ProgressBar of(String name, int total, Logger log) {
26 return new ProgressBar(name, total, 1, 5000, log);
27 }
28
29 public static ProgressBar of(String name, int total, int printIntervalMills, Logger log) {
30 return new ProgressBar(name, total, 1, printIntervalMills, log);
31 }
32
33 public ProgressBar(String name, int total, int step, int printIntervalMills, Logger log) {
34 this.begin = 0;
35 this.end = Math.max(1, total); // >= 1 & 分母 不为0
36 this.step = Math.max(1, step); // >= 1
37 this.printIntervalMills = Math.max(0, printIntervalMills); // >= 0
38 this.name = name != null ? name : "";
39 this.log = log;
40 lastPrintMills = startMills = System.currentTimeMillis();
41 traceId = Integer.toHexString((int) startMills).toUpperCase();
42 }
43
44 public void nextPrint() {
45 next();
46 print();
47 }
48
49 public void next() {
50 begin = Math.min(begin + step, end); // 预防 begin 溢出 end
51 }
52
53 public void print() {
54 if (System.currentTimeMillis() - lastPrintMills < printIntervalMills) return;
55 immediatePrint();
56 }
57
58 public void immediatePrint() {
59 Object[] args = new Object[]{traceId, name, makeProgress(), calcPercent() + "%", begin, end, makeSpendTime()};
60 if (log != null)
61 log.info(String.format(TPL, args));
62 else
63 System.out.printf(TPL + "\n", args);
64 lastPrintMills = System.currentTimeMillis();
65 }
66
67 // ~ private method
68 // --------------------------------------------------------------------------------------------
69 private String makeProgress() {
70 int per = (int) (calcPercent() * 2 / 10); // per <= 20
71 // e.g. [############## ]
72 return PGS.substring(0, per + 1).concat(PGS.substring(23 + per)); // 23: PGS.length / 2 + 1
73 }
74
75 private String makeSpendTime() {
76 int n = (int) ((System.currentTimeMillis() - startMills) / 1000);
77 int h = n / 3600, m = (n % 3600) / 60, s = n % 60;
78 String time = "";
79 if (h > 0) time += (h + "h");
80 if (m > 0) time += (m + "m");
81 if (s > 0) time += (s + "s");
82 return time.isEmpty() ? "0s" : time; // e.g. 1h23m15s、23m15s、15s
83 }
84
85 private float calcPercent() {
86 float per = (begin * 1.0F) / end;
87 if (1.0F - per < 0.00001F) return 100.0F;
88 return Math.round(per * 10000) / 100.0F;
89 }
90}
使用例子:
1// Case 1: 每5s打印一次进度条
2
3ProgressBar pgs = ProgressBar.of("远程请求记录", size, 5000);
4for (Request req: reqs) {
5 // 1)递增步长;2)在满足打印间隔时间后打印进度条
6 pgs.nextPrint();
7
8 // TODO
9}
10
11// Case 2: 分为两步,每5s打印一次进度条
12ProgressBar pgs2 = ProgressBar.of("远程请求记录", size, 5000);
13for (Request req: reqs) {
14 pgs2.next();// 1)仅递增步长
15 pgs2.print();// 2)在满足打印间隔时间后打印进度条
16
17 // TODO
18}
19
20// Case 3: 立即打印进度条
21ProgressBar pgs3 = ProgressBar.of("远程请求记录", size, 5000);
22for (Request req: reqs) {
23 pgs3.next();// 1)单纯递增步长
24 pgs3.immediatePrint();// 2)忽略打印间隔时间,**立即**打印进度条!
25
26 // TODO
27}
使用注意事项:
- 不支持多线程。
- 合理打印。频繁打印同样会影响阅读日志,注意评估打印间隔时间。
(完)