线程安全和不可变性
竞态之发生在多线程访问相同的资源,同时有一个或多个线程写入东西到资源。相反,如果是多线程读取资源的话,反而不会发生竞态。比如
public class ImmutableValue {
private int value = 0;
public ImmutableValue(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
一旦 ImmutableValue
实例创建,你就不能改变它的值。ImmutableValue 是不可变的。你能做的只有通过 getValue()
读取它的值。
如果你要对 ImmutableValue
进行操作,可以通过返回一个新的 ImmutableValue
实例来实现,比如
public class ImmutableValue{
private int value = 0;
public ImmutableValue(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
public ImmutableValue add(int valueToAdd) {
return new ImmutableValue(this.value + valueToAdd);
}
}
引用不是线程安全的!
有一点很重要,即使一个对象是不可变、线程安全的,指向对象的引用也许不是线程安全的。比如这个例子
public class Calculator {
private ImmutableValue currentValue = null;
public ImmutableValue getValue() {
return currValue;
}
public void setValue(ImmutableValue newValue) {
this.currentValue = newValue;
}
public void add(int newValue) {
this.currentValue = this.currentValue.add(newValue);
}
}
Calculator
类持有 ImmutableValue
的实例。可以通过 setValue()
和 add()
方法改变引用。因此,即使Calculator
类在内部持有了 ImmutableValue
对象,也不是它自己的 ImmutableValue
对象了,因此 Calculator
不是线程安全的。换一种方法来说:ImmutableValue
是线程安全的,但是 ImmutableVlue
的使用不是线程安全的。当尝试通过不可变性实现线程安全时,这点需要牢记。
要实现 Calculator
线程安全,可以声明 getValue()
、setValue()
、add()
为 synchronized
。