这个像流利的类并不是严格不可变的,因为字段不是最终的,但它是线程安全的,为什么?
我关心的线程安全问题不是竞争条件,而是变量的可见性。我知道有一种解决方法,使用最终变量和构造函数而不是克隆()+赋值。我只是想知道这个例子是否是一个可行的替代方案。
public class IsItSafe implements Cloneable {
private int foo;
private int bar;
public IsItSafe foo(int foo) {
IsItSafe clone = clone();
clone.foo = foo;
return clone;
}
public IsItSafe bar(int bar) {
IsItSafe clone = clone();
clone.bar = bar;
return clone;
}
public int getFoo() {
return foo;
}
public int getBar() {
return bar;
}
protected IsItSafe clone() {
try {
return (IsItSafe) super.clone();
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
}
}
最佳答案
您在设置字段时没有持有锁,并且正如您提到的那样,该字段不是最终的。
因此,从可见性的角度来看,这种方法不是线程安全的。
这里有一些进一步的说明:https://stackoverflow.com/a/9633968/136247
有关使用 volatile 问题的更新:
为了论证,使用 volatile 修复了这里的线程问题。
但是,您应该重新考虑最终字段和复制构造函数,因为:
- 字段访问会稍微快一些(读取始终可以来自 CPU 缓存)
- 避免不鼓励使用
克隆
(请参阅 Josh Bloch 的《Effective Java》) - 具有 Final 字段的构造函数是不可变类的已知惯用法,代码读者很容易识别
- 将字段标记为
volatile
,同时希望它们不可变,这本身就是一个矛盾;)
关于java - 使用clone()和非final字段的流畅类的线程安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9621260/