0%

CountDownLatch.countDown()问题

现状

有一个跑批业务(几年前的代码了),现在业务提出需求,要对这个跑批里的一种场景做特殊处理,特殊体现在 业务逻辑特殊、跑批频次特殊,因此不好在原跑批里进行杂糅,为此我抽出该特殊场景做单独跑批,该特殊场景和原跑批做绝对隔离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    // 原逻辑
@XxlJob("xxxHandler")
public ReturnT<string> xxxHandler(String param) {
List<String> ids = getIds();
CountDownLatch cdl = new CountDownLatch(ids.size());
for (string id : ids) {
// 处理逻辑...
executor.execute(() -> {
// id 这笔数据逻辑处理....
dealBiz(id,cdl);
});
}
cdl.await();
log.info("执行耗时-->{}",耗时);
return ReturnT.SUCCESS;
}

// 处理业务
private void dealBiz(String id, CountDownLatch cdl){
...
cdl.countDown();
}

对原逻辑修改 v1版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    @XxlJob("xxxHandler")
public Returnt<string> xxxHandler(String param) {
List<string> ids = getids();
CountDownLatch cdl = new CountDownLatch(ids.size());
for (String id : ids) {
// 跳过特殊场景,交给新增的跑批处理
if (isSpecialBiz(id)) {
continue;
}
// 处理逻辑...
executor.execute(() -> {
// id 这笔数据逻辑处理......
dealBiz(id,cdl);
});
}
cdl.await();
log.info("执行耗时-->{}",耗时)
return ReturnT.SUCCESS;
}

// 处理业务
private void dealBiz(String id, CountDownLatch cdl){
...
cdl.countDown();
}

问题

发现问题没?跳过特殊场景的逻辑并没有做countDown()操作,这将会导致这样的情况发生:如果getIds()获得的ids存在特殊场景件,由于跳过时没有countDown(),所以cdl永远不会为0,也就是一直会await()阻塞着。随着时间的推移,await()的线程越来越多,终会oom。

测试环境因为间隔几个小时会自动启动,再加上特殊场景的件不多,且原跑批的频率不高,这个问题在测试阶段并未出现。幸好,上线前发现并修复了。

修复方法

跳过特殊场景时加入 cdl.countDown()

1
2
3
4
if (isSpecialBiz(id)) {
cdl.countDown();
continue;
}

总结

疏忽了!下次遇到CountDownLatch要千万注意!