这段代码不是违反了临界区的互斥吗?

Is not this code violating mutual exclusion in critical section?

我是 Java 的新手,正在尝试了解 Java 中的并发性。在探索时,我在 Java 并发性上非常流行 page 上遇到了这段代码:

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,则允许其他线程读取它。

如有错误请指正

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

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

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

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