代理商是否只持有不可变的值

时间:2013-06-07 10:18:50

标签: concurrency clojure

在Clojure Programming(OReilly)中,有一个示例,其中java.io.BufferedWriter和java.io.Printwriter都放在代理中(每个代理一个代理)。然后将这些内容写入代理操作中。该书说,在代理行动中执行io是安全的。据我所知,在代理行动中所有副作用操作都可以。这是因为提交内的代理操作仅在提交成功时才会运行。其他代理操作中的代理操作仅在外部代理操作成功完成后运行。一般情况下,代理操作可以保证连续应用。

Clojure documentation说:“代理人的状态应该是不可改变的......”。

据我了解,atom和refs必须保存不可变值的原因是clojure可以回滚并重试多次提交。

我不明白的是:

1:如果Clojure确保代理操作只运行一次,为什么代理值必须是不可变的。 (例如,如果我在代理中持有一个java数组,并在代理操作中添加它,这应该没问题,因为该操作只会运行一次。这与向BufferedWriter添加行非常相似)

2:java.io.BufferedWriter被认为是不可变的吗?我知道你可以有一个稳定的引用,但如果代理操作在它上面执行io,它是否仍然被认为是不可变的?

3:如果BufferedWriter被认为是不可变的,我如何判断其他类似的java类是否是不可变的?

2 个答案:

答案 0 :(得分:4)

我认为:

代理人持有的价值应该是“有效不可变的”(从JCIP借来的术语),因为它们在概念上应始终与自己相等。

这意味着,如果我.clone()一个对象并比较两个副本,original.equals(copy)应该是真的,无论我做什么(以及何时)。

从这个意义上说,面对可变性,不能保证充满getter / setter的典型Employee类的实例等于它自己:equals()将被定义为field-by - 场比较,所以测试可能会失败。

但是,BufferedWriter并不代表一个值 - 它的相等性是根据内存中完全相同的对象来定义的。因此它具有“健全”的可变性 - 不像雇员那样 - 这使得它适合将其包装在代理中。

我相信你是正确的,从STM的角度来看,代理价值的可变性不会造成太大的伤害。但它会打破Clojure的时间模型,你不能改变过去的等等。

在决定Java类是否是不可变的时候:如果不深入实现,就不可能。你不必过多关心这个。

我在Java-land中进行以下类型分类:

  • 可变对象(严重地)代表值 - Employee等。永远不要将它们包装在Clojure引用类型中。

  • 表示值的不可变对象 - 它们的不变性反映在doc或命名约定(“EmployeeBuilder”)中。 可以安全地包装任何Clojure参考。

  • 非托管集合类型 - ArrayList等。避免互操作目的。

  • 托管引用/集合类型 - AtomicReference,阻塞队列...... 它们可以与Clojure一起使用,但可疑的是将它们包装在Clojure引用中。

  • 'IO'类型 - BufferedWriter,Swing的东西......你不关心它们的可变性,因为它们根本不代表值 - 你只是想要它们的副作用。 在代理中保护它们以保证访问序列化可能是有意义的。

答案 1 :(得分:2)

代理值应该是不可变的,因为有人可以这样做:

 (def my-agent (agent (BufferedWriter.)))
 (.write @my-agent "Hello world")

这基本上是在不经过代理控制机制的情况下修改代理值(在本例中是编写器)。

是的,BufferedWriter是可变的,因为写入它可以改变其内部状态。它就像一个指针或引用而不是value.