java – 为什么需要同步?
发布时间:2020-12-15 04:52:10 所属栏目:Java 来源:网络整理
导读:我试图在我的 Java线程知识??中填补一些可耻的空白,而我正在阅读Brian Goetz等人(强烈推荐的BTW)中的Java Concurrency in Practice,本书中的一个早期例子给我留下了一个问题.在下面的代码中,我完全理解为什么在更新hits和cacheHits成员变量时需要同步,但为什
我试图在我的
Java线程知识??中填补一些可耻的空白,而我正在阅读Brian Goetz等人(强烈推荐的BTW)中的Java Concurrency in Practice,本书中的一个早期例子给我留下了一个问题.在下面的代码中,我完全理解为什么在更新hits和cacheHits成员变量时需要同步,但为什么在读取hits变量时需要getHits方法呢?
第2章的示例代码: public class CachedFactorizer extends GenericServlet implements Servlet { private BigInteger lastNumber; private BigInteger[] lastFactors; private long hits; 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); }... 我有一种感觉它与原子性,监视器和锁定有关,但我并不完全理解这些,所以请有人在那里解释一下吗? 提前致谢… 詹姆士 解决方法
这里有许多潜在的问题.迈克尔指出了一个大的(长商店的非原子性),但还有另一个.在没有’发生在’之前的关系(在释放和获取锁之间提供,例如在同步块中)之间,可以无序地看到写入.
请注意,行命中位于service()中的cacheHits之前.在没有同步的情况下,JVM完全有权以一种可能使其他线程混淆的方式重新排序这些指令.例如,它可以在命中之前重新排序cacheHits,或者它可以使增加的cacheHits值在增加的命中值之前对其他线程可见(在这种情况下区别并不重要,因为结果可能相同) .想象一下重新排序,从干净的缓存开始,导致以下交错: Thread 1 Thread 2 --------------- ---------------- ++cacheHits (reordered) cacheHits=1,hits=0 read hits (as 0) ++hits cacheHits=1,hits=1 read cacheHits (as 1) calculate 1 / 0 (= epic fail) 你不会得到你期望的结果,这是肯定的. 请注意,这很容易调试.您可以进行1000次service()调用,然后读取线程将cacheHits视为500并命中为1. 50,000%的缓存命中率可能不太明显,对于糟糕的调试器来说更加困惑. 同步读取会设置之前发生的关系,因此不会发生这种情况,然后锁定会提供其他人提到的其他优点. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |