不可变对象一定是线程安全的
那么什么是不可变对象呢?
当满足一下条件时,对象才是不可变的。
- 对象创建以后其状态就不能修改
- 对象的所有域都是final类型
- 对象是正确创建的(在对象的创建期间,this引用没有逸出)
我们回到之前因数分解的例子,通过构造一个不可变类来实现同步:
class OneValueCache{
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
public OneValueCache(BigInteger i, BigInteger[] factors){
lastNumber = i;
lastFactors = Arrays.copyOf(factors, factors.length);
}
public BigInteger[] getFactors(BigInteger i){
if (lastNumber == null || !lastNumber.equals(i)) {
return null;
} else {
return Arrays.copyOf(lastFactors, lastFactors.length);
}
}
}
public class VolatileCachedFactorizer implements Servlet{
private volatile OneValueCache cache =
new OneValueCache(null,null);
public void service(ServletRequest req, ServletResponse resp){
BigInteger i = extractFromRequest(req);
BigInteger[] factors = cache.getFactors(i);
if (factors == null) {
factors = factor(i);
cache = new OneValueCache(i, value);
}
encodeIntoResponse(resp, factors);
}
}
我们可以看到OneValueCache为一个不可变类,并且我们在创建时,使用volatile关键字声明,这样保证了线程安全性和可见性。
但要注意的一点是:
如果final类型的域所指向的是可变对象,那么在访问这些域所指向的对象的状态时仍然需要同步
public final class ThreeStooges {
private final Set<String> stooges = new HashSet<String>();
public ThreeStooges() {
stooges.add("Moe");
stooges.add("Larry");
stooges.add("Curly");
}
public boolean isStooge(String name) {
return stooges.contains(name);
}
public synchronized Set<String> getStooges(){
return stooges;
}
}
如上,stooges变量为可变对象,因此,如果我们需要对其访问时,需要加上synchronized关键字来保证同步。