这个代码如何实现线程安全?为什么需要实现线程安全?

5
这是我的代码...
@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))
            return null;
        else 
            return Arrays.copyOf(lastFactors, lastFactors.length);
    }
}

@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);
        if(factors==null){   // ---> line 1
            factors=factor(i);  // --> line 2
            cache=new OneValueCached(i, factors);
        }

        encodeIntoResponse(resp,factors);
    }
}

为什么根据书籍中的说法,VolatileCachedFactorizer类是线程安全的,但我的观点是:
1.在第1行如果2个线程同时到达该点,第一个线程检查条件并发现factor=null,第二个线程也在同一时间检查相同的条件,在第一个线程暂停执行第2行之后,发现factor=null。并且两者都将创建新的OneValueCached对象,那么这段代码如何是线程安全的?书中认为这是线程安全的。

谢谢


@threadSafe 是什么注解?它要么是一个打字错误,要么不是标准注解,因为标准命名约定应该是 @ThreadSafe(对于 @immutable 也是一样)。 - Thomas
仅供注意,我知道这不是标准注释... - Sumit Singh
2
return lastFactors=Arrays.copyOf(lastFactors, lastFactors.length); 看起来很奇怪。它做了一个假设性的内部数组副本,但是然后将内部状态重新赋值为它提供的同一个副本。这不再是防御性或不可变的。实际上,这是一个编译错误,因为 lastFactors 是 final,对吗? - Thilo
1
你能够包含可以编译的代码吗?你在哪里定义了“cached”? - Peter Lawrey
Cached很可能是指cache。但是factor方法又是什么呢? - Thilo
显示剩余3条评论
5个回答

4

它是线程安全的,因为lastNumberlastFactors之间永远不会存在不一致,这可能导致错误的因式分解。它并不保证将进行最少量的因式分解: OneValueCached 可能会被创建多次,但仍然是线程安全的。


2
“线程安全”的确切概念在此未被定义。为了使该代码“线程安全”,您期望发生/不发生什么?
  1. 如果您期望涉及的所有对象(就我在这段代码中所看到的)的状态保持一致,则它是“线程安全”的(如@artbristol所解释的那样)。
  2. 如果您期望在同时调用“service”时只创建一个“Cache”对象,则它不是“线程安全”的。

同样,在没有针对此情况的“线程安全”行为的定义的情况下,我们无法确定。


0

这段代码是线程安全的。但我认为构造函数中的Array.copyOf()并不是必要的。


0

虽然这是一个一年多前的问题,但我仍然想给出我的解释。 实际上,这段代码是从《Java并发编程实战》第40页中采用的。作者声称VolatileCachedFactorizer是一个线程安全的servlet,因为该servlet程序本身被用户同时调用。所以作者的意思是,如果变量--OneValueCached cache可以被线程一致地读取和写入,也就是不会导致lastNumber和lastFactors不匹配的情况,那么它将是线程安全的。我感到困惑的是,这本书声称只有使用Arrays.copyOf()函数才能保证线程安全。但是为什么?为什么需要Arrays.copyOf()呢?


0
Qisen,回答你的问题,Arrays.copyOf()是必需的,否则OneValueCache将不是不可变的。如果在构造OneValueCache时没有复制因子,那么你会让数组的引用(直到那个点仅局限于运行线程)逃逸。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接