You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ThreadPoolExecutor中的竞态条件问题排查与修复求助

ThreadPoolExecutor下Bot实例竞态条件问题求助

代码要求

  • 同步执行Bot#run方法

需要修复的问题

  • 不同Bot实例与线程间的资源共享(如Bot.responseData等)冲突

Bot.run()执行流程

  1. 发起HTTP请求从服务器获取随机值(如f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454,服务器始终返回随机值)
  2. 将值存入Bot.responseData
  3. 以Bot.responseData为参数发起HTTP请求,获取JSON结果
  4. 将JSON存入Bot.statistics
  5. 清空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工具本身的线程问题。

修复步骤

  1. 确保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();
        }
    }
    
  2. 移除实例变量,改用局部变量传递状态
    把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();
            }
        }
    }
    

    局部变量为线程私有,无需同步即可避免状态冲突。

  3. 优化任务提交逻辑
    原循环批量提交任务可能导致请求过于密集,引发连接池异常。改为每个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 13:18:10