ThreadPoolExecutor中的竞态条件问题排查与修复求助
ThreadPoolExecutor下Bot实例竞态条件问题求助
代码要求
- 同步执行
Bot#run方法
需要修复的问题
- 不同Bot实例与线程间的资源共享(如
Bot.responseData等)冲突
Bot.run()执行流程
- 发起HTTP请求从服务器获取随机值(如
f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454,服务器始终返回随机值) - 将值存入
Bot.responseData - 以
Bot.responseData为参数发起HTTP请求,获取JSON结果 - 将JSON存入
Bot.statistics - 清空
Bot.responseData = null
相关代码
Bot实体
public class Bot { private final BotStatistic statistic = new BotStatistic(); private final Object mutex = new Object(); private String responseData; void run() { synchronized(mutex) { String myHost = "http://myhost"; firstCall(myHost); secondCall(myHost); cleanup(); } } private void firstCall(String host) { // first http call -> responseData = "f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454"; } private void secondCall(String host) { // second http call(responseData) -> // JSON {"success": true, "requestValue": "f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454"} statistic.add(json); } private void cleanup() { this.responseData=null; } }
BotRunner实体
public class BotRunner implements Runnable { private final Bot bot; public BotRunner(Bot bot) { this.bot = bot; } @Override public void run() { bot.run(); } }
执行方式
//init executor LocalDateTime termination = LocalDateTime.now().plusSeconds(5L); while (LocalDateTime.now().isBefore(termination)) { for (Bot bot : bots) { executor.execute(new BotRunner(bot)); } } //shutdown & close executor
当前现象
执行后发现1-2%的不同Bot实例出现重复的Bot.responseData,示例如下:
thread-1 : start (Bot@44g35) thread-1 : f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454 thread-2 : start (Bot@898g) thread-2 : f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454 thread-1 : end thread-2 : end
期望结果
每个Bot实例获取唯一的随机值,示例如下:
thread-1 : start (Bot@44g35) thread-1 : f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454 thread-2 : start (Bot@898g) thread-2 : 018b2f19-e79e-7d6a-a56d-29feb6211b04 thread-1 : end thread-2 : end
问题分析与修复方案
问题根源
当前Bot类的synchronized(mutex)已保证单个实例的run()不会被多线程并发执行,但不同实例出现重复随机值的核心原因是HTTP请求工具存在线程不安全的共享状态——比如复用了线程不安全的请求对象、共享了未隔离的响应处理逻辑,导致不同线程的请求互相干扰,拿到重复响应值。实例变量responseData的同步保护无法解决HTTP工具本身的线程问题。
修复步骤
确保HTTP请求工具线程安全
使用线程安全的HTTP客户端(如Apache HttpClient的CloseableHttpClient可全局复用),但每个请求必须新建独立的请求对象(HttpGet/HttpPost),禁止共享请求实例。示例修正firstCall:private void firstCall(String host) { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpGet request = new HttpGet(host + "/get-random-value"); try (CloseableHttpResponse response = httpClient.execute(request)) { this.responseData = EntityUtils.toString(response.getEntity()); } } catch (IOException e) { e.printStackTrace(); } }移除实例变量,改用局部变量传递状态
把responseData改为run()方法的局部变量,彻底消除实例内的状态共享风险,同时可移除不必要的同步锁:public class Bot { private final BotStatistic statistic = new BotStatistic(); void run() { String myHost = "http://myhost"; String randomValue = firstCall(myHost); if (randomValue != null) { secondCall(myHost, randomValue); } } private String firstCall(String host) { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpGet request = new HttpGet(host + "/get-random-value"); try (CloseableHttpResponse response = httpClient.execute(request)) { return EntityUtils.toString(response.getEntity()); } } catch (IOException e) { e.printStackTrace(); return null; } } private void secondCall(String host, String randomValue) { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpPost request = new HttpPost(host + "/submit-value"); request.setEntity(new StringEntity("{\"value\":\"" + randomValue + "\"}")); request.setHeader("Content-Type", "application/json"); try (CloseableHttpResponse response = httpClient.execute(request)) { String json = EntityUtils.toString(response.getEntity()); statistic.add(json); } } catch (IOException e) { e.printStackTrace(); } } }局部变量为线程私有,无需同步即可避免状态冲突。
优化任务提交逻辑
原循环批量提交任务可能导致请求过于密集,引发连接池异常。改为每个Bot实例独立循环执行,控制请求频率:// 替代原执行逻辑 ExecutorService executor = Executors.newFixedThreadPool(bots.size()); LocalDateTime termination = LocalDateTime.now().plusSeconds(5L); for (Bot bot : bots) { executor.submit(() -> { while (LocalDateTime.now().isBefore(termination)) { bot.run(); Thread.sleep(100); // 可选:添加间隔避免请求过载 } }); } executor.shutdown(); executor.awaitTermination(6, TimeUnit.SECONDS);
验证
修复后运行测试,检查不同Bot实例的随机值是否唯一,同时监控HTTP请求日志,确认每个请求都获取到服务器返回的独立随机值。
内容的提问来源于stack exchange,提问作者IndependenceCR
相关产品推荐
相关产品推荐

