轮询必须线程安全,因高并发下普通int会导致索引重复或跳过;AtomicInteger通过CAS实现无锁原子递增,语义清晰、性能优,是首选;AtomicReference虽可替代但需手动CAS、Integer不可变致GC压力大,不推荐用于单纯计数。

AtomicReference 在 Spring Cloud LoadBalancer 的轮询算法中并不直接参与默认的节点选择逻辑,但它在自定义轮询实现时,是替代 AtomicInteger 实现线程安全计数的合理选择。
为什么轮询需要线程安全?
微服务请求并发量高,多个线程可能同时调用 loadBalancer.choose()。若用普通 int 计数器,会出现重复取同一索引或跳过实例的问题,导致流量倾斜。必须保证“取索引→递增”这一操作的原子性。
AtomicInteger 用 CAS 操作实现无锁递增,是最常见方案;而 AtomicReference 可以封装一个整数值对象(如 new AtomicInteger(0)),或直接持有 Integer 对象并原子更新——但后者因 Integer 不可变,每次 update 都新建对象,开销略大。
AtomicReference 能否替代 AtomicInteger 做轮询计数?
可以,但不推荐作为主选。原因如下:
立即学习“Java免费学习笔记(深入)”;
- AtomicReference
每次 increment 需要手动读取、计算、compareAndSet,代码更冗长,易出错 - Integer 是不可变对象,每次更新都生成新实例,增加 GC 压力
- AtomicInteger 提供了 getAndIncrement()、incrementAndGet() 等专为计数优化的原子方法,语义清晰、性能更好
Spring Cloud LoadBalancer 默认轮询怎么做的?
官方 RoundRobinLoadBalancer 使用 Reactor 的 Flux + atomic integer state(内部基于 AtomicInteger 封装),并非 AtomicReference。它通过 Mono.defer() 和状态流转实现响应式轮询,核心仍是 CAS 计数,只是封装在 reactor.core.publisher.Operators 中,对外不可见。
如果你要手写一个同步轮询逻辑(比如配合 RestTemplate + DiscoveryClient),推荐写法是:
private static final AtomicInteger counter = new AtomicInteger(0); // ... int index = counter.getAndIncrement() % instances.size(); ServiceInstance instance = instances.get(index);
什么场景下会用到 AtomicReference?
当轮询逻辑需要携带更多上下文状态时,AtomicReference 就显出优势。例如:
- 实现带权重的轮询:封装一个包含 currentWeight、totalWeight、instances 列表的自定义状态类,用 AtomicReference
统一更新 - 实现粘性会话(sticky session):保存上一次选中的 instance 引用,用 AtomicReference
缓存并原子替换 - 动态刷新实例列表时,用 AtomicReference
- > 替换整个列表,避免读写竞争


















