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

Java中仅单线程更新不可变类引用、多线程读取是否需要加锁?

package com.blah;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;

import org.springframework.stereotype.Component;

import lombok.Value;

@Component
public class AdminCredentialsProvider {

    @Value
    public class AuthInfo {
        String authToken;
        String xsrfToken;
    }

    private AuthInfo adminAuthInfo = null;

    private final ScheduledExecutorService adminAuthInfoRefresher =
            Executors.newSingleThreadScheduledExecutor();

    //This will be called by multiple threads
    public AuthInfo getSiteAdminCredentials() {
        return new AuthInfo(adminAuthInfo.getAuthToken(), adminAuthInfo.getXsrfToken());
    }

    @PostConstruct
    public void init() {
        adminAuthInfo = loginAsSiteAdmin();

        Runnable runnable = () -> {
            //Do I need to protect this assignment with a lock at all
            adminAuthInfo = loginAsSiteAdmin();
        };

        adminAuthInfoRefresher.scheduleAtFixedRate(runnable, 1, 1, TimeUnit.HOURS);
    }

    private AuthInfo loginAsSiteAdmin() {
        //call rest endpoint to login
        return new AuthInfo("mockToken", "mockXsrf");
    }
}

结论

这个场景不需要加锁,但现有代码存在可见性缺陷,仅需做轻量调整即可满足并发安全要求,无需引入ReentrantLock、ReadWriteLock这类重量同步工具。

详细分析

  1. 前置条件已经规避了大部分并发风险
  • AuthInfo是Lombok @Value注解修饰的不可变类,所有字段默认带final修饰,实例初始化后属性不会变更,天然线程安全。
  • adminAuthInfo的引用赋值操作只有两个执行节点:Spring初始化Bean时的主线程赋值、后续单线程调度器的定时更新,不存在多线程写竞争,而Java的引用赋值本身就是原子操作,不会出现半赋值的中间状态。
  1. 现有代码的唯一问题是可见性保障缺失
    普通成员变量的修改没有happen-before规则兜底,调用getSiteAdminCredentials的业务线程可能迟迟感知不到adminAuthInfo的更新,长期读取到旧的凭证信息。

  2. 最优修复方案(二选一即可,都不需要加锁)

  • 给adminAuthInfo加volatile修饰:volatile的写操作对后续所有读操作可见,刚好匹配单线程写、多线程读的场景,没有额外性能损耗。
  • 把adminAuthInfo替换为AtomicReference<AuthInfo>:本质和volatile原理一致,原子的get/set操作也能完全满足需求。
  1. 加锁属于过度设计
    锁的核心作用是解决多线程竞争下的原子性保障问题,这个场景没有多线程写竞争,仅有的写操作本身就是原子的,加锁只会带来不必要的性能开销,没有实际意义。

额外优化点

当前getSiteAdminCredentials方法中不需要重新new AuthInfo对象,既然AuthInfo本身不可变,直接返回adminAuthInfo引用即可,减少不必要的对象创建。

内容的提问来源于stack exchange,提问作者arahant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 08:24:05