加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

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%的缓存命中率可能不太明显,对于糟糕的调试器来说更加困惑.

同步读取会设置之前发生的关系,因此不会发生这种情况,然后锁定会提供其他人提到的其他优点.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读