Java Concurrency在实践中的示例如何是线程安全的

时间:2017-12-08 06:33:07

标签: java multithreading concurrency

下面是一本关于线程安全类的 Java Concurrency in Practice(清单2.8)的示例。

我的问题是下面的类是如何线程安全的?

例如,如果两个线程Thread AThread B进入CachedFactorizer的服务方法。 Thread B后跟Thread A。现在,如果Thread A正在执行第一个同步块,Thread B显然等待对象的内部锁定。如果Thread BThread A进入第二个同步块之前进入第一个同步块,则它将查看过时值,并且可能此条件称为竞争条件

那么,我的理解就在这里吗?或者我对并发缺乏一些基本的了解?

@ThreadSafe
public class CachedFactorizer implements Servlet {

    @GuardedBy("this") private BigInteger lastNumber;

    @GuardedBy("this") private BigInteger[] lastFactors;

    @GuardedBy("this") private long hits;

    @GuardedBy("this") private long cacheHits;

    public synchronized long getHits() { return hits; }

    public synchronized double getCacheHitRatio() {

        return (double) cacheHits / (double) hits;

    }


    public void service(ServletRequest req, ServletResponse resp) {

        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = null;
        synchronized (this) {
            ++hits;
            if (i.equals(lastNumber)) {
                ++cacheHits;
                factors = lastFactors.clone();
            }
        }
        if (factors == null) {
            factors = factor(i);
            synchronized (this) {
                lastNumber = i;
                lastFactors = factors.clone();
            }
        }
        encodeIntoResponse(resp, factors);
    }
}

4 个答案:

答案 0 :(得分:0)

在退出后第一个块中使用的唯一东西是因子克隆(),因此不会受到进入第一个或第二个块的任何其他线程的影响。

答案 1 :(得分:0)

从技术上讲,您可以为同一个号码设置任意数量的缓存未命中数,从而导致factor(i)多次连续i。这永远不会影响结果,因为所有共享变量访问都是同步的,因此代码是线程安全的。

我会说这是non-critical race condition,因为在实际编程中你不希望同时在同一个任务上工作。但是,由于这是一个例子,我想这是可以理解的。它也会使这个例子更加复杂。

在链接的问题中,已经发现此示例因不必要地使用clone()而感到困惑。没有理由克隆数组,因为它们在任何地方都没有被修改。

答案 2 :(得分:0)

这个类是线程安全的,因为所有共享变量都是在同步块中访问的,在这种情况下没有数据竞争:

  

“如果程序包含两个冲突的访问(第17.4.1节),这些访问不是由先发生关系排序的,则称其包含数据争用。https://docs.oracle.com/javase/specs/jls/se9/html/jls-17.html

问题是从商业角度来看这种行为是否有效,例如。如果两个线程在第一次同步blok之后相遇,则它们可以以不同的顺序执行第二个块。

答案 3 :(得分:0)

如果我删除两个clone()有什么区别?例如:factor = lastFactors.clone();我认为它仍然有效。

public void service(ServletRequest req, ServletResponse resp) {

    BigInteger i = extractFromRequest(req);
    BigInteger[] factors = null;
    synchronized (this) {
        ++hits;
        if (i.equals(lastNumber)) {
            ++cacheHits;
            factors = lastFactors; //not cloned
        }
    }
    if (factors == null) {
        factors = factor(i);
        synchronized (this) {
            lastNumber = i;
            lastFactors = factors;//not cloned
        }
    }
    encodeIntoResponse(resp, factors);
}