[英]How to check the size() or isEmpty() for ConcurrentLinkedQueue
我正在嘗試為Java中的Web爬網程序設計簡單的結構原型。 到目前為止,原型僅嘗試執行以下操作:
對於起始URL隊列,我正在使用ConcurrentLinkedQueue
進行同步。 要生成新線程,我正在使用ExecutorService
。
但是在創建新線程時,應用程序需要檢查ConcurrentLinkedQueue
是否為空。 我嘗試使用:
.size()
.isEmpty()
但是兩者似乎都沒有返回ConcurrentLinkedQueue
的真實狀態。
問題在下面的塊中:
while (!crawler.getUrl_horizon().isEmpty()) {
workers.submitNewWorkerThread(crawler);
}
因此,即使輸入只有2個URL,ExecutorService也會在其限制內創建所有線程。
這里實現多線程的方式是否有問題? 如果不是,那么檢查ConcurrentLinkedQueue狀態的更好方法是什么?
該應用程序的入門班:
public class CrawlerApp {
private static Crawler crawler;
public static void main(String[] args) {
crawler = = new Crawler();
initializeApp();
startCrawling();
}
private static void startCrawling() {
crawler.setUrl_visited(new HashSet<URL>());
WorkerManager workers = WorkerManager.getInstance();
while (!crawler.getUrl_horizon().isEmpty()) {
workers.submitNewWorkerThread(crawler);
}
try {
workers.getExecutor().shutdown();
workers.getExecutor().awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void initializeApp() {
Properties config = new Properties();
try {
config.load(CrawlerApp.class.getClassLoader().getResourceAsStream("url-horizon.properties"));
String[] horizon = config.getProperty("urls").split(",");
ConcurrentLinkedQueue<URL> url_horizon = new ConcurrentLinkedQueue<>();
for (String link : horizon) {
URL url = new URL();
url.setURL(link);
url_horizon.add(url);
}
crawler.setUrl_horizon(url_horizon);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Crawler.java
,用於維護URL隊列和已訪問URL的集合。
public class Crawler implements Runnable {
private ConcurrentLinkedQueue<URL> url_horizon;
public void setUrl_horizon(ConcurrentLinkedQueue<URL> url_horizon) {
this.url_horizon = url_horizon;
}
public ConcurrentLinkedQueue<URL> getUrl_horizon() {
return url_horizon;
}
private Set<URL> url_visited;
public void setUrl_visited(Set<URL> url_visited) {
this.url_visited = url_visited;
}
public Set<URL> getUrl_visited() {
return Collections.synchronizedSet(url_visited);
}
@Override
public void run() {
URL url = nextURLFromHorizon();
scrap(url);
addURLToVisited(url);
}
private URL nextURLFromHorizon() {
if (!getUrl_horizon().isEmpty()) {
URL url = url_horizon.poll();
if (getUrl_visited().contains(url)) {
return nextURLFromHorizon();
}
System.out.println("Horizon URL:" + url.getURL());
return url;
}
return null;
}
private void scrap(URL url) {
new Scrapper().scrap(url);
}
private void addURLToVisited(URL url) {
System.out.println("Adding to visited set:" + url.getURL());
getUrl_visited().add(url);
}
}
URL.java
只是一個具有private String url
並覆蓋了hashCode()
和equals()
。
此外, Scrapper.scrap()
到目前為止還具有虛擬實現:
public void scrap(URL url){
System.out.println("Done scrapping:"+url.getURL());
}
WorkerManager
創建線程:
public class WorkerManager {
private static final Integer WORKER_LIMIT = 10;
private final ExecutorService executor = Executors.newFixedThreadPool(WORKER_LIMIT);
public ExecutorService getExecutor() {
return executor;
}
private static volatile WorkerManager instance = null;
private WorkerManager() {
}
public static WorkerManager getInstance() {
if (instance == null) {
synchronized (WorkerManager.class) {
if (instance == null) {
instance = new WorkerManager();
}
}
}
return instance;
}
public Future submitNewWorkerThread(Runnable run) {
return executor.submit(run);
}
}
之所以最終創建的線程比隊列中的URL多,是因為(很可能)Executor的所有Thread都沒有啟動,直到您多次經過while
循環為止。
每當使用線程時,都應牢記線程是獨立調度的,並以其自己的速度運行,除非您明確同步它們。 在這種情況下,線程可以在之后的任何時間開始submit()
調用,即使它看起來你想每一個開始,現在已經超過nextURLFromHorizon
在你的下一次迭代之前while
循環。
在將Runnable
提交給執行程序之前,請考慮從隊列中使URL出隊。 我還建議定義一個CrawlerTask
,該CrawlerTask
一次提交給執行器,而不是重復提交的Crawler
。 在這種設計中,您甚至不需要線程安全的容器即可抓取URL。
class CrawlerTask extends Runnable {
URL url;
CrawlerTask(URL url) { this.url = url; }
@Override
public void run() {
scrape(url);
// add url to visited?
}
}
class Crawler {
ExecutorService executor;
Queue urlHorizon;
//...
private static void startCrawling() {
while (!urlHorizon.isEmpty()) {
executor.submit(new CrawlerTask(urlHorizon.poll());
}
// ...
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.