问题描述
在最近的项目中,因为需要多次对某个中台接口进行反复调用,以获取分页数据的全量数据以便在后台进行去重,我写了一个多线程功能代码如下:
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); 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.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();
|
经过验证同样可行。