我正在编写一个多线程的webcrawler,其中有一个WebCrawler
对象,它使用ExecutorService来处理WebPage
并从每个页面中提取锚点。我在WebCrawler
类中定义了一个方法,WebPage
可以调用该方法,将提取的子链接添加到WebCrawler
的{{1}}集,并且该方法当前看起来像这样:
nextPagestoVisit
目前我正在使用同步方法。但是,我正在考虑其他可能的选择。
使Set成为synchronizedSet:
public synchronized void addSublinks(Set<WebPage> sublinks) {
this.nextPagestoVisit.addAll(sublinks);
}
使集合易变:
public Set<WebPage> nextPagestoVisit = Collections.synchronizedSet(new HashSet<WebPage>());
这两种选择都是否足够? (我假设同步方法方法已经足够)。或者我是否必须将它们与其他安全措施结合起来?如果他们都工作,哪一个是最好的方法?如果其中一个或两个都不起作用,请简要说明原因(即,哪种情况会导致问题)。感谢
编辑:要明确,我的目标是确保如果两个public volatile Set<WebPage> nextPagestoVisit = new HashSet<WebPage>();
都尝试同时添加其子链接,则一次写入不会被另一次写入覆盖(即所有子链接都将成功被添加到Set)。
答案 0 :(得分:0)
我不确定你知道volatile关键字实际上做了什么。它不能确保相互排斥。引自here:
“另一方面,使用volatile会强制所有对volatile内存变量的访问(读取或写入)发生在主内存中,从而有效地将volatile变量保留在CPU缓存之外。这对某些操作非常有用。只需要变量的可见性是正确的,访问顺序并不重要。“
你确实有几种选择:
使用同步块
synchronized {
//synchronized code
}
使用信号量等替代方案
Semaphore semaphore,
semaphore.aquire()
...
semaphore.release()
再次注意,您说您正在尝试实现同步访问。如果你只需要确保变量是最新的变量,那么volatile就是一个相当简单的解决方案。
答案 1 :(得分:0)
Making the variable that holds the set volatile will do nothing for you. For a start this only affects the "pointer" to the set, not the set itself. Then it means the atomic updates to the pointer will be seen by all threads. It does nothing for the Set.
Making the Set
a synchronizedSet
does what you want. As would either synchronized blocks or Semaphores. However both would add more boilerplate than just using synchronizedSet
and are an additional vector for bugs.