java - 代码是线程安全的吗?

标签 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的副本。我说得对吗?

最佳答案

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

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

当缓存值更改时,cache 变量会自动更改,因为它是 volatile 。可能在某一时刻使用了两个 OneValueCached 实例:调用一个实例的方法,然后执行线程更改,创建一个新实例并更改 cache 引用指向一个新实例,但它不会影响其他线程和旧实例,在方法调用返回之前,旧实例不符合垃圾回收条件(执行方法中的 this 变量保持它还活着)。

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

关于java - 代码是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33844905/

相关文章:

java - Maven 插件测试中 org.apache.maven.repository.RepositorySystem 的组件查找异常

java - 多线程 - 如何安全地从 ArrayList 中删除?

java - 如何更改计时器每次运行的延迟?

java - 设备中的应用名称

java - 如何正确分割非转义分隔符?

multithreading - cl-actors 返回值

java - 同时读写 ConcurrentHashMap 的值对象

python - 使用线程获取文件中每个单词的计数

java - Dining Philosophers 代码中发生的饥饿

java - 监听某些变化并将其更新到数据库