Java Popsicle不可变

时间:2012-10-24 18:16:37

标签: java concurrency immutability

我正在研究一个问题,我需要为问题加载大量输入,并处理这些输入以创建“问题空间”(即构建允许有效访问输入的数据结构等)。一旦完成初始化,多线程进程就会以并发方式广泛使用有组织/已处理的输入。

出于性能原因,我不想在并发阶段锁定和同步所有读取操作。我真正想要的是一个不可变的对象,可以安全地同时访问多个读者。

出于实际原因(可读性和可维护性)我不想让InputManager成为真正的不可变对象(即所有字段'final'并在构造中初始化)。 InputManager将具有许多数据结构(列表和映射),其中每个对象中的对象彼此具有许多循环引用。这些对象构造为“真正的”不可变对象。我不想为InputManager建立一个14参数构造函数,但是我确实需要InputManager类在构造之后提供一致的只读视图。

我想要的是'冰棒不变性',正如Eric Lippert所讨论的那样here.

我采用的方法依赖于使用所有变异方法的“包可见性”,并在单个包中执行所有可变操作(即构造InputManager)。吸气剂都具有公众可见度。

类似的东西:

public final class InputManager {  // final to prevent making mutable subclasses 
    InputManager() { ... } //package visibility limits who can create one
        HashMap<String,InputA> lookupTable1;
        ...

    mutatingMethodA(InputA[] inputA) { //default (package visibility)
        //setting up data structures...
    }

    mutatingMethodB(InputB[] inputB) { //default (package visibility)
        //setting up data structures...
    }

    public InputA getSpecificInput(String param1) {
        ... //access data structures
        return objA; //return immutable object
    }
}

总体思路,如果我还不够清楚的话,我将在单个线程中构造InputManager,然后将其传递给将使用该对象进行并发工作的多个线程。我想尽可能强制执行这个“两阶段”可变/不可变对象生命周期,而不是做一些太“可爱”的事情。寻找关于更好地实现这一目标的方法的评论或反馈,因为我确信这不是一个不常见的用例,但我找不到支持它的设计模式。

感谢。

3 个答案:

答案 0 :(得分:1)

我认为您可以为两个阶段分别设置单独的界面。一个用于建筑部分,另一个用于阅读部分。这样,您可以干净地分离访问模式。您可以将此视为interface segregation principle (pdf)

的实例
  

不应强迫客户端依赖他们不使用的接口。

答案 1 :(得分:1)

只要对象安全发布,读者就不能改变它。

“发布”在这里表示创建者如何使对象可供读者使用。例如,创建者将其放入阻塞队列,读者正在轮询队列。

这取决于您的发布方法。我敢打赌这是一个安全的。

答案 2 :(得分:1)

就个人而言,我会坚持使用你简单而充分的方法,但如果你感兴趣的话,就会有一个可变的伴侣成语。您编写了一个具有mutators的内部类,同时重用了封闭实例中的所有字段和getter。

一旦你失去了可变的伴侣,它留下的封闭实例就是真正不可改变的。