Lambda及新线程中AtomicInteger自增值异常与并行请求完成判断问题
原因分析
这是线程并发执行导致日志输出顺序与实际操作顺序不一致,同时代码存在冗余逻辑:
AtomicInteger本身是线程安全的,无需额外synchronized块:incrementAndGet()是原子操作,加锁完全多余,反而增加线程调度开销,但这不是日志异常的直接原因。- 日志输出与原子操作不构成原子性步骤:
- Thread-275和Thread-272几乎同时读取到
countOfResponse的值为0,先后输出before_inc_count = 0。 - Thread-275先执行
incrementAndGet(),将值从0变为1,但还没来得及输出after_inc_count,Thread-272就已经执行了incrementAndGet(),把值从1变为2。 - 之后Thread-275才输出
after_inc_count = 2,紧接着Thread-272也输出相同值。 - 本质是读取原子变量、执行自增、输出日志这三个步骤没有被原子化,两个线程的操作交叉执行,导致日志中看不到中间值1。
- Thread-275和Thread-272几乎同时读取到
- finally块的判断逻辑存在线程安全问题:多个线程可能同时读取到
countOfResponse等于10,导致重复输出完成提示。
正确实现方案
推荐两种可靠的实现方式:
方式1:使用CountDownLatch(最直观)
CountDownLatch专门用于等待一组线程完成任务,逻辑清晰且高效:
CountDownLatch latch = new CountDownLatch(hostInfoList.size()); LOGGER.info("myMethod: hostInfoList_size = {}", hostInfoList.size()); for (HostInfo hostInfo : hostInfoList) { new Thread(() -> { ErrorDetails errorDetails = new ErrorDetails(); XML response = null; try { LOGGER.info(""); response = execute_network_request(); } catch (Exception e) { errorDetails.setDetails(e.getMessage()); } finally { LOGGER.info("myMethod: finally"); latch.countDown(); } }).start(); } // 阻塞等待所有线程完成 try { latch.await(); LOGGER.info("myMethod: finished all requests"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
方式2:修正原AtomicInteger逻辑
如果坚持使用AtomicInteger,需确保自增和判断的原子性,同时去掉冗余锁:
AtomicInteger countOfResponse = new AtomicInteger(0); int totalRequests = hostInfoList.size(); LOGGER.info("myMethod: hostInfoList_size = {}", totalRequests); for (HostInfo hostInfo : hostInfoList) { new Thread(() -> { ErrorDetails errorDetails = new ErrorDetails(); XML response = null; try { LOGGER.info(""); response = execute_network_request(); } catch (Exception e) { errorDetails.setDetails(e.getMessage()); } finally { LOGGER.info("myMethod: finally"); // 自增后直接判断,确保只有最后一个完成的线程触发提示 int currentCount = countOfResponse.incrementAndGet(); if (currentCount == totalRequests) { LOGGER.info("myMethod: finished all requests"); } } }).start(); }
内容的提问来源于stack exchange,提问作者Alexei
相关产品推荐
相关产品推荐

