【问题标题】:Is not this code violating mutual exclusion in critical section?这段代码不是在临界区违反互斥吗?
【发布时间】:2015-03-06 13:36:39
【问题描述】:

我是 Java 新手,并试图了解 Java 中的并发性。在探索时,我在一个非常流行的page 上发现了这段代码,关于 Java 并发:

public class CrawledSites {
  private List<String> crawledSites = new ArrayList<String>();
  private List<String> linkedSites = new ArrayList<String>();

  public void add(String site) {
    synchronized (this) {
      if (!crawledSites.contains(site)) {
        linkedSites.add(site);
      }
    }
  }


/**
   * Get next site to crawl. Can return null (if nothing to crawl)
   */

  public String next() {
    if (linkedSites.size() == 0) {
      return null;
    }
    synchronized (this) {
      // Need to check again if size has changed
      if (linkedSites.size() > 0) {
        String s = linkedSites.get(0);
        linkedSites.remove(0);
        crawledSites.add(s);
        return s;
      }
      return null;
    }
  }

}

我认为这里的函数 next() 违反了互斥,如下几行:

if (linkedSites.size() == 0) {
  return null;
}

被保存在同步块之外,因此如果某个线程在 add() 或 next() 中修改同步块内的linkedSites,则允许其他线程读取它。

如果我错了,请纠正我。

【问题讨论】:

  • 你没有错。对列表的每次访问都应该同步。

标签: java multithreading concurrency synchronized synchronized-block


【解决方案1】:

你是对的 - 我认为代码作者可能认为他们做了一些聪明的事情,通过在进入同步部分之前检查 linkedSites 数组是否为空来节省一点时间。这看起来很安全,因为在同步部分中再次检查了大小。

但是,Java 内存模型不保证调用 next() 的线程将看到与修改它的最后一个线程处于相同状态的linkedSites,除非读取也在同步部分中完成,因此理论上调用 next() 的线程可能尽管另一个线程已将数据放入其中,但仍将数组视为空。每个线程都可能拥有自己的对象数据副本,该副本仅通过同步代码块与其他线程的副本同步。因此,调用 next 的线程可能会错误地将数组视为空。

【讨论】:

    【解决方案2】:

    严格意义上的互斥是对的。但即使在多线程程序中,也没有必要在访问时将 all 同步。阅读。我不知道整个程序,但next() 可能会被调用几次。如果线程错过了某些条目,则可能其他一些条目稍后会捕获它。但是,正如您所说,不能保证其他人会看到这些更改。

    【讨论】:

      【解决方案3】:

      linkedSites.size() 应该在同步块内,否则它可能看不到其他线程对linkedSites 所做的更改。

      【讨论】:

        猜你喜欢
        • 2015-10-08
        • 2011-07-08
        • 1970-01-01
        • 1970-01-01
        • 2010-10-22
        • 1970-01-01
        • 2015-07-19
        • 2013-08-07
        • 2010-10-27
        相关资源
        最近更新 更多