Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

问题描述

在最近的项目中,因为需要多次对某个中台接口进行反复调用,以获取分页数据的全量数据以便在后台进行去重,我写了一个多线程功能代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
pageList.forEach(obj -> {
pool.execute(() -> {
request.setCurrent(obj);
log.info(JSON.toJSONString(threadRequest));
JSONObject threadJsonObject = JcommonFeign.querylightninglnfluencelnfo(JSON.parseObject(JSON.toJSONString(threadRequest)));
log.info("查询结束");
MiddleGroundResponse threadResponse = SON.parseObject(SON.toJSONString(threadJsonObject), MiddleGroundResponse.class);
threadList.add(JSON.toJSONString(threadResponse.getResult().getRecords()));
latch.countDown();
});
});
latch.await();

但是调用之后,经常发生重复调用后一页数据的情况,例如调用了2、3次第一页,导致获取的总数一致,但数据内容存在差异,前端展示内容不断变化。

问题原因

每个线程都从同一个 request 对象开始工作,最后将结果存储到 threadList 中。经过排查是由以下两个因素引起的:

共享的 request 对象:

多线程中共享了同一个 request 对象,而没有为每个线程创建一个独立的副本。这可能导致每个线程在执行 commonFeign.querylightninglnfluencelnfo 方法时,使用的都是相同的输入数据。如果 request 的内容在某个线程中被修改,那么其他线程可能会看到这种修改,导致调用时使用了相同或错误的数据。

线程安全问题:

request 对象在多个线程之间共享,而没有进行任何同步操作来确保线程安全。如果 request 对象是可变的,并且多个线程同时修改它的内容,可能会导致未定义的行为,从而出现重复或错误的调用。

解决方案

为每个线程创建独立的 request 副本:在 forEach 循环中,为每个线程创建一个新的 request 对象的副本,而不是共享同一个对象。这样可以确保每个线程都使用独立的数据。

方法一

1
2
3
4
5
6
7
8
9
10
11
12
13
pageList.forEach(obj -> {
RequestObject threadRequest = new RequestObject(request); // 创建request副本
pool.execute(() -> {
request.setCurrent(objJ);
log.info(JSON.toJSONString(threadRequest));
JSONObject threadJsonObject = commonFeign.querylightninglnfluencelnfo(JSON.parseObject(JSON.toJSONString(threadRequest)));
log.info("查询结束");
MiddleGroundResponse threadResponse = SON.parseObject(SON.toJSONString(threadJsonObject), MiddleGroundResponse.class);
threadList.add(JSON.toJSONString(threadResponse.getResult().getRecords()));
latch.countDown();
});
});
latch.await();

而我使用的是Clone方法,通过使用 clone 方法,可以为每个线程创建独立的 request 副本,从而避免在多线程环境下出现重复调用相同数据的情况。

方法二

1
2
3
4
5
6
7
8
9
10
11
12
13
pageList.forEach(obj -> {
pool.execute(() -> {
RequestObject threadRequest = (RequestObject) request.clone();// 创建request副本
request.setCurrent(objJ);
log.info(JSON.toJSONString(threadRequest));
JSONObject threadJsonObject = commonFeign.querylightninglnfluencelnfo(JSON.parseObject(JSON.toJSONString(threadRequest)));
log.info("查询结束");
MiddleGroundResponse threadResponse = SON.parseObject(SON.toJSONString(threadJsonObject), MiddleGroundResponse.class);
threadList.add(JSON.toJSONString(threadResponse.getResult().getRecords()));
latch.countDown();
});
});
latch.await();

经过验证同样可行。

评论