不可变性和可重新配置的配置

时间:2015-09-15 02:32:24

标签: java multithreading concurrency thread-safety immutability

请注意:虽然这个问题提到了Java,但我认为这是一个OOP /并发性问题,可能会被具有丰富编程经验的任何人回答。

所以我正在构建一个ConfigurationLoader,它将从远程服务中读取Configuration POJO,并通过API提供。一些事情:

  • 第一次向ConfigurationLoader询问Configuration时,后台线程(工作人员)将每隔30秒对远程服务进行ping操作以获取更新,然后应用这些更新到Configuration实例;和
  • 如果Configuration被修改,后台工作人员将收到更改通知,并将“新”Configuration推送到远程服务;
  • ConfigurationLoaderConfiguration都必须是线程安全的

因此,当我认为“线程安全”时,我首先想到的是 immutability ,这引导我走向像Immutables这样的优秀项目。问题是Configuration不能是不可变的,因为我们需要能够在客户端更改它,然后让加载程序将这些更改发送回服务器。

我的下一个想法是尝试同时生成ConfigurationLoaderConfiguration单例,但问题是ConfigurationLoader需要大量参数来实例化它,并且{ {3}},在构造中接受参数的单例不是真正的单例。

// 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'
        }
    }
}

我有什么选择?如何创建加载程序和配置是线程安全的,其中配置可由客户端(按需)更改或异步更改后台工作者线程?

2 个答案:

答案 0 :(得分:2)

  

问题是Configuration不能是不可变的,因为我们需要能够改变它

它仍然可以是不可变的,你只需为每次更改创建一个新的(“copy-on-write”)。

  

我有什么选择?

首先要考虑的事情是:您希望如何对并发运行的任务中的配置更改做出反应?基本上,您有三种选择:

在任务完成之前忽略配置更改

即。您的代码将文件写入的某个目录 - 完成将当前文件写入当前目标目录,将新文件放入新目录中。如果您从未创建该文件,那么将一些字节写入/new/path/somefile将不是一个好主意。对此最好的选择可能是存储在任务实例字段中的不可变Configuration对象(即创建任务时 - 在这种情况下,为了清晰起见,您也可以创建该字段final)。如果您的代码被设计为孤立的小任务的集合,这通常最有效。

优点:配置永远不会在单个任务中发生变化,因此这很容易保证安全且易于测试。

缺点:配置更新永远不会使它已经运行任务。

让您的任务检查配置更改

即。您的任务会定期将一些数据发送到电子邮件地址。为您的配置设置一个中央存储(如伪代码),并在任务代码中的某个时间间隔(即收集数据和发送邮件之间)重新获取它。这通常最适合长期运行/永久性任务。

优点:配置可以在任务运行期间进行更改,但仍然有点安全 - 只需确保您有一些内存障碍来读取配置(使您的私有{{1} } configuration,使用volatile,用锁保护它,等等。

缺点:任务代码比第一个选项更难测试。检查之间的配置值可能仍然过时。

信号配置更改为您的任务

基本上是选项二,但反过来。每当配置更改时,中断您的任务,将处理中断作为“配置需要更新”消息,使用新配置继续/重新启动。

优点:配置值永远不会过时。

缺点:这是最难做到的。有些人甚至认为你不能做到这一点,因为中断只应用于任务堕胎。如果您将任务的更新检查放在正确的位置,那么第二个选项只有非常小的好处(如果有的话)。如果你没有充分的理由,不要这样做。

答案 1 :(得分:1)

你需要一个单身人士来解决这个问题,但你的单身人士不是不可改变的事情。它是线程安全的东西。使您的单例(配置)包含一个简单的属性对象,并通过同步保护对此的访问。您的配置加载器以某种方式知道此配置单例和函数,以便在检测到更改时在同步下设置Properties对象的新实例。

我非常确定Apache Commons Configuration会做这样的事情。