sno*_*ape 5 concurrency clojure
在Clojure Programming(OReilly)中,有一个示例,其中java.io.BufferedWriter和java.io.Printwriter都放在代理中(每个代理一个代理).然后将这些内容写入代理操作中.该书说,在代理行动中执行io是安全的.据我所知,在代理行动中所有副作用操作都可以.这是因为提交内的代理操作仅在提交成功时才会运行.其他代理操作中的代理操作仅在外部代理操作成功完成后运行.一般来说,代理操作可以保证连续应用.
Clojure 文档说:"代理的状态本身应该是不可变的......".
据我所知,原子和引用必须保持不可变值的原因是clojure可以回滚并重试多次提交.
我不明白的是:
1:如果Clojure确保代理操作只运行一次,为什么代理值必须是不可变的.(例如,如果我在代理中持有一个java数组,并在代理操作中添加它,这应该没问题,因为该操作只会运行一次.这与向BufferedWriter添加行非常相似)
2:java.io.BufferedWriter被认为是不可变的吗?我知道你可以有一个稳定的引用,但如果代理操作在它上面执行io,它是否仍然被认为是不可变的?
3:如果BufferedWriter被认为是不可变的,我如何判断其他类似的java类是否是不可变的?
照我看来:
代理人所持有的价值观应该是“有效地不可变的”(从 JCIP 借用的术语),因为它们在概念上应该始终与自身相同。
这意味着,如果我.clone()一个对象并比较两个副本,则original.equals(copy)无论我做什么(以及何时),都应该是正确的。
从这个意义上说,面对可变性,充满 getter/setter 的典型类的实例Employee不能保证等于其自身:equals()将被定义为逐字段比较,因此测试可能会失败。
然而 BufferedWriter 并不代表一个值 - 它的相等性是根据内存中完全相同的对象来定义的。因此,它具有“健全”的可变性——与 Employee 不同——这使得它适合将其包装在代理中。
我相信你是对的,从 STM 的角度来看,代理值可变性不会造成太大伤害。但它会打破 Clojure 的时间模型,在该模型中你“无法改变过去”等等。
判断一个 Java 类是否是不可变的:如果不深入实现,那是不可能的。不过你不必太关心这个。
我会对 Java 领域的类型进行以下分类:
(糟糕地)表示值的可变对象 -Employee等。切勿将它们包装在 Clojure 引用类型中。
代表值的不可变对象 - 它们的不可变性反映在文档或命名约定(“EmployeeBuilder”)中。可以安全地包装在任何 Clojure 参考中。
非托管集合类型 - ArrayList 等。除非出于互操作目的,否则应避免使用。
托管引用/集合类型 - AtomicReference、阻塞队列...它们与 Clojure 配合得很好,但将它们包装在 Clojure 引用中是值得怀疑的。
“IO”类型 - BufferedWriter、Swing 之类的东西...您不关心它们的可变性,因为它们根本不代表值 - 您只需要它们的副作用。在代理中保护它们以保证访问序列化可能是有意义的。