下面是一本关于线程安全类的 Java Concurrency in Practice(清单2.8)的示例。
我的问题是下面的类是如何线程安全的?
例如,如果两个线程Thread A
和Thread B
进入CachedFactorizer的服务方法。 Thread B
后跟Thread A
。现在,如果Thread A
正在执行第一个同步块,Thread B
显然等待对象的内部锁定。如果Thread B
在Thread 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);
}
}
答案 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);
}