最近在写一个配置中心的客户端,碰到一个问题,没有想到特别好的解决方案,和大家讨论下。
问题是这样的:
当配置发生变更时,我想自动更新 spring bean 中的 @Value 字段。 看了下有一些开源实现用的是基于反射的方式,通过设置 field 的值来实现更新
但是这样会有个问题,就是配置更新的线程修改了 field 的值,并不能保证用户其他线程可以看见最新的更改 (如果 field 上没有加 volatile,synchronized 这些保证可见性的关键字)。 但是不少开源实现好像都没有关注这个问题。
我能想到的方法:
最后的问题:
1
seaswalker 2020-07-02 20:40:57 +08:00
讲道理在 X86 平台上这种修改就是可见的,根本就不需要 volatile....
|
2
MoHen9 2020-07-02 20:41:58 +08:00 via Android 1
想多了,你都需要动态修改配置了,还在乎一致性问题?如果真的考虑一致性,应该使用其他方式保证,比如缓存或 zk,而不是单纯的加锁和 volatile,一般服务可能会有集群,或者多个服务使用了相同的配置,这些服务之间的一致性需要保证吗?
这种动态切换配置的做法一般在开发或部署时会用,甚至极端情况也会,这时候的一致性没有那么重要。 |
3
sagaxu 2020-07-02 20:47:39 +08:00 via Android
@seaswalker X86 上也不能保证吧
|
4
seaswalker 2020-07-02 20:56:53 +08:00 1
@sagaxu #3 我觉得除了写成下面这种不加 volatile 的循环导致编译器提升优化之外,x86 上应该是可见的
写过几个例子测试过这个问题: https://github.com/seaswalker/JDK/issues/8 楼主的场景编译器应该不会做这种优化,也可能我理解的有问题 |
5
Samuelcc OP @MoHen9 我倒不是想保证强一致性,只是想有一种保证,比如说配置刷新完成,这时候我可以认为这个实例中所有线程看到的配置都是最新的。
如果不加 volatile,就无法做出保证,可能很久过去了,看到的还是旧版配置。 |
6
Samuelcc OP @seaswalker 学到了新知识,谢谢
|
7
xiangyuecn 2020-07-02 21:15:16 +08:00
一个 56 秒的操作,你在这纠结 0.0001ms ?
|
8
sagaxu 2020-07-02 21:23:42 +08:00 via Android 1
@seaswalker 你用了 System.out ,那玩意儿会加锁,约束比用了 volatile 更强
|
9
Samuelcc OP @xiangyuecn 不是时间的问题,只是想有保证,例如事务显示执行完成,这时候可以有保证不会丢失,而不是显示执行完成,但是可能在某个将来才落盘,哪怕这个将来在绝大多数情况下都很快
|
10
sagaxu 2020-07-02 21:40:41 +08:00 via Android
@seaswalker 除了加锁同步之外,你这段代码还有其他致命问题,根本不能用来验证并发问题。
|
11
javapythongo 2020-07-02 21:42:31 +08:00 via iPhone
spring 自带刷新,可以去了解下 @RefreshScope
|
12
momocraft 2020-07-02 21:45:33 +08:00
实验只能证明并发不安全 不能证明并发安全
|
13
Samuelcc OP @javapythongo 这个我了解过,但是 spring 的 refresh 机制问题比较多,尤其是在 spring cloud 1.x 下,两次出线程泄漏的问题,不想用这个
|
14
ipwx 2020-07-02 21:53:32 +08:00
@seaswalker 我觉得不一定吧。那种双 CPU 架构的服务器机器。。。
|
15
seaswalker 2020-07-02 22:36:18 +08:00
@sagaxu #8 我又写了个新的例子
这次只是简单的加法,可以试一下,加上 JVM 参数-Xint,就会三秒后停止,去掉就会死循环,这不就说明了是 JIT 编译优化的锅? 况且虽然打印中有锁,但子线程对 flag 的修改没有加锁,也不满足 happens-before 吧,我的理解是打印恰恰也起到了阻止 JIT 优化的作用 |
16
sagaxu 2020-07-02 22:56:26 +08:00 via Android
@seaswalker 首先,你的理解是错的。其次,即便你的理解是对的,难道要靠禁止 jit 来保证可见性?
|