我在這里遇到一個非常奇怪的錯誤,我不太明白。就目前而言,我正在制作一個爬蟲程式,它使用 ForkJoinTasks 掃描頁面上的鏈接,然后也爬取這些鏈接。但是,當我使用 .join() 收集任務時,有時會得到 ConcurrentModificationExceptions 指向該行。
Spider[] newSpiders = crawlForURLs("a", "href").stream()
.filter(url -> SpiderUtils.isLinkURLValid(url))
.map(url -> new Spider(url).fork()).toArray(Spider[]::new);
for (Spider c : newSpiders) {
Image[] crawledImages = c.join();
}
有問題的行是 c.join()。我嘗試將它包圍在 ReentrantLock 中,只是想看看會發生什么,但這并沒有什么不同。
完整堆疊跟蹤:https ://pastebin.com/qCJte65Q
代碼行是:在 com.eulerity.hackathon.imagefinder.Spider.compute (Spider.java:85) ( Image[] crawledImages = c.join();)
在 com.eulerity.hackathon.imagefinder.ImageFinder.doPost (ImageFinder.java:45)images = SpiderUtils.commonPool.invoke(baseSpider);
老實說,我無法弄清楚為什么會這樣,但是如果您需要查看我的代碼的另一部分,請不要猶豫。謝謝!
uj5u.com熱心網友回復:
我相信,問題出在你的SpiderUtils班上。更具體地說,這種方法:
public static boolean isLinkURLValid(URI url) { return url.getHost().equalsIgnoreCase(baseUrl.getHost()) && !containsURL(linksCrawled, url) && !containsURL(linksInProgress, url) && !url.getPath().contains("."); }
而這個方法:
public static boolean containsURL(ArrayList<URI> urls, URI left) { return urls.stream().anyMatch(right -> urlsMatch(left, right)); }
這兩種方法都不是synchronized,這意味著多個執行緒可以同時執行它們。對于該containsURL(...)方法來說,這可能不是問題。鑒于它的作用,它沒有理由存在synchronized(它和urlsMatch(...)方法都不依賴任何外部狀態)。問題在于isLinkURLValid(...)方法如何傳遞給方法。linksCrawledlinksInProgresscontainsURL(...)
這些串列變數是靜態的,因此相同的實體被傳遞給isLinkURLValid(...)不同執行緒上的方法。如果您所做的只是流式傳輸這些串列的元素(即,僅從串列中讀取),這將是可以的。但是,在您的代碼的其他地方,您還可以將元素添加到這些串列中(即,您寫入串列)。這些寫入是在synchronized方法中執行的,但這并不重要,因為您正在讀取串列而不在同一個物件上同步。因此,您的代碼容易受到ConcurrentModificationExceptions 的攻擊(您不能在迭代時修改非并發集合)。
我相信解決方案是簡單地制作isLinkURLValid(...)方法synchronized,但我不是 100% 肯定的,因為我沒有測驗過。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/488416.html
