这个流畅的类不是严格不可变的,因为字段不是最终的,但它是线程安全的,为什么?
我关注的线程安全问题不是竞争条件,而是变量的可见性。我知道有一个解决方法使用final变量和构造函数而不是clone()+赋值。我只是想知道这个例子是否可行。
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);
}
}
}
答案 0 :(得分:1)
由于可见性问题,此thread非常一致,因为该类不是线程安全的。
为什么你说这个课不是一成不变的?类的状态由foo和bar定义,对于任何特定实例,一旦创建实例,就不能从类外部更改。所以它是不可变的,即使字段没有明确地声明为final。
foo和bar更改的唯一位置(在foo()和bar()方法中),更改是在局部变量上完成的,根据定义,一次只能由一个线程访问。
修改强>
我认为这是Java Concurrency in Practice(3.3.2)中定义的Stack Confinement的一个例子,它使foo()和bar()方法线程安全,因为clone
不允许逃避完全构建之前的方法。
局部变量本质上局限于执行的三元组;它们存在于执行线程的堆栈中,其他线程无法访问它。
答案 1 :(得分:1)
您在设置字段时没有按住锁定,正如您自己提到的那样,该字段不是最终字段。
因此,从可见性的角度来看,这种方法不是线程安全的。
这里有一些进一步的澄清:https://stackoverflow.com/a/9633968/136247
有关使用volatile的问题的更新:
为了使用volatile修复的参数,这里的线程问题 但是,您应该重新考虑最终字段和复制构造函数,因为:
clone
(请参阅Josh Bloch的Effective Java)volatile
,同时打算使它们不可变,这本身就是一个矛盾;)