代码线程安全吗?

时间:2015-11-21 15:07:43

标签: java multithreading

我在StackOverflow上查看了有关此代码段的一些问题,但没有一个提到我发现的问题。

以下是代码:

@immutable // This is not a standard annotation .Only for Showing that behavior of Class 
class OneValueCached{
    private final BigInteger lastNumber;
    private final BigInteger[] lastFactors;
    public OneValueCached(BigInteger i,BigInteger[] factors){
        lastNumber=i;
        lastFactors=Arrays.copyOf(factors, factors.length);
    }

    public BigInteger[] getFactors(BigInteger i){
        if(lastNumber==null || !lastNumber.equals(i))   // ---> line 2
            return null;
        else 
            return Arrays.copyOf(lastFactors, lastFactors.length);   // ---> line 3
    }
}

@threadSafe // This is not a standard annotation .Only for Showing that behavior of Class 
public class VolatileCachedFactorizer implements Servlet{
    private volatile OneValueCached cache=new OneValueCached(null, null);

    public void service(ServletRequest req, ServletResponce resp){
        BigInteger i= extractFromRequest(req);
        BigInteger[] factors=cache.getFactors(i);   // ---> line 1
        if(factors==null){
            factors=factor(i);
            cache=new OneValueCached(i, factors);   // ---> line 4
        }

        encodeIntoResponse(resp,factors);
    }
}

想象一下,

线程A到达第1行,并调用cache.getFators(BigInteger i),它来到第2行,条件语句返回 false

然后线程B到达第1行,并且还调用cache.getFators(BigInteger i),当它到达第2行时,条件语句返回 true 。因此,线程B继续,并且到第4行,将变量cache更改为新的。

线程A继续,到第3行,返回错误的结果!

那有什么不对?这段代码是线程安全的吗? (根据 Java Concurrency in Practice 一书,是的,它是线程安全的)

更新

我正在考虑当线程B将cache的值更改为新值时,线程仍然可以返回先前对象的lastFactors的副本。我是对的吗?

1 个答案:

答案 0 :(得分:3)

是的,这段代码是线程安全的。

由于OneValueCached是不可变的,因此它的实例永远不会返回错误的结果,假设它是用正确的值构造的。要么返回正确的结果,要么返回null

更改缓存值后,cache变量会自动更改,因为它是volatile。可能是在某个时间使用了两个OneValueCached个实例:调用一个实例的方法,然后执行线程更改,创建一个新实例并更改cache引用以指向一个新实例,但它不会影响其他线程和旧实例,它不会有资格进行垃圾收集,直到方法调用返回(执行方法中的this变量使其保持活动状态)。 / p>

此外,在cache中存储不必要的实例没有特别的效率损失,因为无论如何都需要创建实例,并且从性能的角度来看,实际存储cache中的引用是无关紧要的。实现缓存的不同方式(例如能够存储多个值)可以进一步提高性能,但这样做仍然没有比没有缓存更糟糕(内存使用模式的差异无关紧要),即使在最坏的情况下(缓存的值从未使用过。)