synchronized修饰静态方法时锁的是当前类的Class对象,所有线程串行执行该方法,与实例无关;实例方法锁this,二者互不干扰,静态同步方法仅保证方法体串行执行,不能保护非static字段。

Java中用synchronized类锁实现全局白名单的高效刷新,核心在于**避免锁粒度粗导致性能瓶颈,同时确保多线程下白名单更新的原子性与可见性**。单纯用synchronized(MyClass.class)加锁虽能保证线程安全,但会成为高并发场景下的串行瓶颈。真正高效的方案,是将“锁”与“刷新逻辑”解耦,用类锁只保护**状态切换和引用替换**这两个关键动作,而非整个加载过程。
用类锁保护白名单引用的原子替换
白名单本质是一个不可变或线程安全的集合(如CopyOnWriteArrayList、ConcurrentHashMap,或更推荐的Set.copyOf()生成的不可变Set)。刷新时不应在原集合上增删,而应构建新集合后,用类锁一次性替换引用:
- 加载新白名单数据(可异步、可缓存、可校验),生成新的不可变Set
- 用
synchronized(WhitelistManager.class)包裹引用赋值:currentWhitelist = newWhitelist; - 这样锁内仅执行毫秒级的指针替换,不阻塞数据加载和旧数据读取
读操作完全无锁,靠volatile或final保障可见性
白名单引用本身需声明为volatile(JDK 5+内存模型保证写后读可见),或封装在静态final容器中(如static final AtomicReference<Set<String>>):
- 查询方法直接调用
currentWhitelist.contains(key),零同步开销 - 无需synchronized、无需ReentrantLock,靠引用可见性 + 集合本身的线程安全性协同工作
- 若用
Collections.unmodifiableSet()或Set.of()构造不可变集,还能杜绝意外修改
刷新触发与调度分离,避免类锁被长时间占用
类锁绝不用于IO、网络请求或复杂计算。刷新任务应由独立线程(如ScheduledExecutorService)驱动:
立即学习“Java免费学习笔记(深入)”;
- 定时拉取配置中心/DB/文件,或监听ZooKeeper/Nacos变更事件
- 数据解析、校验、构建新Set等耗时操作在锁外完成
- 仅最后一步“替换引用”进入类锁临界区——通常
- 即使刷新失败,旧白名单仍可用,具备天然降级能力
补充:比类锁更轻量的替代选择
如果项目已引入现代并发工具,可考虑更精准的控制:
-
AtomicReference<Set<String>>+compareAndSet():无锁替换,语义更清晰 -
StampedLock乐观读:读多写少场景下吞吐更高(但需处理重试逻辑) - Spring环境可用
@RefreshScope+ 事件驱动,把刷新逻辑交给框架管理 - 类锁不是必须项——它只是最易理解、零依赖的起点
不复杂但容易忽略:高效的关键不在“怎么锁”,而在“锁什么”和“锁多久”。类锁守住的是引用切换那一刻的确定性,其余全部放开,白名单自然就既安全又快了。


















