Java 不变性和可重新加载配置
请注意:尽管这个问题提到了Java,但我认为这本质上是一个OOP/并发问题,任何具有丰富编程经验的人都可能回答这个问题Java 不变性和可重新加载配置,java,multithreading,concurrency,thread-safety,immutability,Java,Multithreading,Concurrency,Thread Safety,Immutability,请注意:尽管这个问题提到了Java,但我认为这本质上是一个OOP/并发问题,任何具有丰富编程经验的人都可能回答这个问题 因此,我正在构建一个ConfigurationLoader,它将从远程服务读取ConfigurationPOJO,并通过API使其可用。有几件事: 一旦ConfigurationLoader第一次被要求进行配置,后台线程(工作线程)将每隔30秒ping远程服务一次,以进行更新,然后将这些更新应用于配置实例;及 如果配置被修改,后台工作人员将收到更改通知,并将“新的”配置推送
因此,我正在构建一个
ConfigurationLoader
,它将从远程服务读取Configuration
POJO,并通过API使其可用。有几件事:
- 一旦
第一次被要求进行ConfigurationLoader
,后台线程(工作线程)将每隔30秒ping远程服务一次,以进行更新,然后将这些更新应用于配置
实例;及配置
- 如果
被修改,后台工作人员将收到更改通知,并将“新的”配置
推送到远程服务李>配置
和ConfigurationLoader
都必须是线程安全的Configuration
配置
不能是不变的,因为我们需要能够在客户端对其进行更改,然后让加载程序将这些更改反馈给服务器
我的下一个想法是尝试使ConfigurationLoader
和Configuration
都成为单例,但问题是ConfigurationLoader
需要很多参数来实例化它,因此,在构造中接受参数的单例并不是真正的单例
// Groovy pseudo-code
class Configuration {
// Immutable? Singleton? Other?
}
class ConfigurationLoader {
// private fields
Configuration configuration
ConfigurationLoader(int fizz, boolean buzz, Foo foo, List<Bar> bars) {
super()
this.fizz = fizz
this.buzz = buzz;
// etc.
}
Configuration loadConfiguration() {
if(configuration == null) {
// Create background worker that will constantly update
// 'configuration'
}
}
}
//Groovy伪代码
类配置{
//不可变?单例?其他?
}
类配置加载器{
//私人领域
配置
ConfigurationLoader(int-fizz、boolean-buzz、Foo-Foo、列表栏){
超级()
this.fizz=fizz
this.buzz=buzz;
//等等。
}
配置loadConfiguration(){
if(配置==null){
//创建将不断更新的后台工作程序
//“配置”
}
}
}
我的选项是什么?如何创建线程安全的加载程序和配置,其中配置可以由客户端(按需)更改,也可以由后台工作线程异步更改?您需要一个单例来实现这一点,但单例并不是不变的。这是线程安全的东西。使您的单例(配置)包含一个简单的属性对象或其他内容,并通过同步保护对此对象的访问。您的配置加载器不知怎的知道这个配置单例,并且在同步下,当它检测到更改时,可以设置Properties对象的新实例 我很确定ApacheCommons配置会执行类似的操作 问题是配置不能是不变的,因为我们需要能够更改它 它仍然是不可变的,您只需为每个更改创建一个新的(“写时复制”) 我的选择是什么 首先要考虑的是:在并发运行的任务中,您希望如何对配置更改做出反应?基本上,您有三种选择: 忽略配置更改,直到任务完成 例如,代码将文件写入的某个目录-将当前文件写入当前目标目录后,将新文件放入新目录。如果您从未创建过该文件,那么将一些字节写入
/new/path/somefile
将不是一个好主意。最好的选择可能是将一个不可变的配置
对象存储在任务实例的字段中(即,在任务创建时-在这种情况下,为了清晰起见,您还可以将该字段设置为final)。如果您的代码被设计为一组孤立的小任务,那么这通常效果最好
优点:配置永远不会在一个任务中改变,所以这很容易得到安全和易于测试
缺点:配置更新不会使其成为已经运行的任务
让您的任务检查配置更改
也就是说,您的任务定期向电子邮件地址发送一些数据。为您的配置(如伪代码)提供一个中央存储,并在某个时间间隔(即从收集数据到发送邮件之间)从任务代码中重新获取它。这通常最适用于长时间运行/永久性任务
优点:配置可以在任务运行期间更改,但仍然有点简单,以确保安全-只需确保您有一些用于读取配置的内存屏障(使您的私有配置
字段易失性
,使用原子引用
,用锁保护它,等等)
缺点:任务代码比第一个选项更难测试。检查之间的配置值可能仍然过期
任务的信号配置更改
基本上是方案二,但反过来说。每当配置更改时,中断您的任务,将中断处理为“配置需要更新”消息,继续/重新启动新配置
优点:配置值永远不会过时
缺点:这是最难做到的。有些人甚至可能会争辩说,您无法正确执行此操作,因为中断只应用于任务中止。如果您将任务的更新检查放在正确的位置,那么与第二个选项相比,只有很小的好处(如果有的话)。如果你没有充分的理由,就不要这样做