有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

用于在java中同步对集合的访问的多线程选项

我正在编写一个多线程webcrawler,其中有一个WebCrawler对象,它使用ExecutorService处理WebPage并从每个页面提取锚。我在WebCrawler类中定义了一个方法,它可以被WebPage调用,将提取的子链接添加到WebCrawlernextPagestoVisit集合中,该方法当前如下所示:

public synchronized void addSublinks(Set<WebPage> sublinks) {
    this.nextPagestoVisit.addAll(sublinks);
}

目前我使用的是同步方法。不过,我正在考虑其他可能的选择

  1. 使集合成为同步集合:

    public Set<WebPage> nextPagestoVisit = Collections.synchronizedSet(new HashSet<WebPage>());
    
  2. 使设置不稳定:

    public volatile Set<WebPage> nextPagestoVisit = new HashSet<WebPage>();
    

这两种选择本身是否足够?(我假设同步方法已经足够了)。或者我必须将它们与其他安全措施结合起来?如果它们都有效,哪种方法是最好的方法?如果其中一个或两个都不起作用,请简要说明原因(即,什么样的情况会导致问题)。谢谢

编辑:明确地说,我的目标是确保如果两个WebPage同时尝试添加其子链接,则一个写入不会被另一个覆盖(即,所有子链接都将成功添加到集合中)


共 (2) 个答案

  1. # 1 楼答案

    使保存集合的变量变为volatile对您没有任何帮助。首先,这只影响指向集合的“指针”,而不影响集合本身。这意味着指针的原子更新将被所有线程看到。它对场景没有任何作用

    使SetasynchronizedSet符合您的要求。与同步块或Semaphore一样。但是,两者都会添加比仅使用synchronizedSet更多的样板文件,并且是bug的额外向量

  2. # 2 楼答案

    我不确定您是否知道volatile关键字的实际用途。它不能确保相互排斥。从here引用:

    另一方面,使用volatile会迫使对volatile变量的所有访问(读或写)发生在主内存中,从而有效地将volatile变量从CPU缓存中清除。这对于一些只要求变量的可见性正确且访问顺序不重要的操作非常有用

    但是,您确实有几种选择:

    • 使用同步块

      synchronized {
      //synchronized code
      }
      
    • 使用信号量等替代方法

      Semaphore semaphore,
      semaphore.aquire()
      ...
      semaphore.release()
      

    再次注意,您是说您正在尝试实现同步访问。如果您只需要确保变量是最新的,那么volatile始终是一个相当简单的解决方案