Java ExecutorService Runnable不更新值

时间:2015-11-12 23:25:03

标签: java concurrency runnable executorservice java.util.concurrent

我正在使用Java下载其URL存储在数据库中的网站的HTML内容。我也想把他们的HTML放到数据库中。 我正在使用Jsoup来实现此目的:

public String downloadHTML(String byLink) {
        String htmlInPage = "";
        try {
            Document doc = Jsoup.connect(byLink).get();
            htmlInPage = doc.html();
        } catch (org.jsoup.UnsupportedMimeTypeException e) {
            // process this and some other exceptions
        } 
        return htmlInPage;
    }

我想同时下载网站并使用此功能:

public void downloadURL(int websiteId, String url, 
                        String categoryName, ExecutorService executorService) {
   executorService.submit((Runnable) () -> {
       String htmlInPage = downloadHTML(url);
       System.out.println("Category: " + categoryName + " " + websiteId + " " + url);
       String insertQuery = 
              "INSERT INTO html_data (website_id, html_contents)  VALUES (?,?)";
       dbUtils.query(insertQuery, websiteId, htmlInPage);   
   });
}

dbUtils是我的基于Apache Commons DbUtils的类。详情如下:http://pastebin.com/iAKXchbQ
我正以这种方式使用上面提到的所有内容:( List<Object[]>详细信息也在pastebin上解释)

public static void main(String[] args) {
        DbUtils dbUtils = new DbUtils("host", "db", "driver", "user", "pass");
        List<String> categoriesList = 
                     Arrays.asList("weapons", "planes", "cooking", "manga");
        String sql = "SELECT lw.id, lw.website_url, category_name " +
                "FROM list_of_websites AS lw JOIN list_of_categories AS lc " +
                "ON lw.category_id = lc.id " +
                "where category_name = ? ";

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (String category : categoriesList) {
            List<Object[]> sitesInCategory = dbUtils.select(sql, category );
            for (Object[] entry : sitesInCategory) {
                int websiteId = (int) entry[0];
                String url = (String) entry[1];
                String categoryName = (String) entry[2];
                downloadURL(websiteId, url, categoryName, executorService);
            }
        }
        executorService.shutdown();
}

我不确定这个解决方案是否正确但是有效。现在我想修改代码以保存HTML不是来自我的数据库中的所有网站,而只是修改每个类别中的固定数量。
例如,从“武器”类别下载和保存50个网站的HTML,从“飞机”类别下载50个。我认为没有必要为此目的使用sql:如果我们为每个类别选择50个站点,它不会'我的意思是我们将它们全部保存起来,因为语法和连接问题可能不正确。
我尝试使用字段Runnablecounter创建单独的实现maxWebsitesPerCategory的类,但这些变量不会更新。另一个想法是创建字段Map<String,Integer> sitesInCategory而不是counter,将每个类别作为键放在那里并递增其值,直到达到maxWebsitesPerCategory,但它也不起作用。请帮我!
P.S:我也很感激任何与我实现并发下载相关的建议(我之前没有使用Java中的并发,这是我的第一次尝试)

1 个答案:

答案 0 :(得分:0)

这个怎么样?

for (String category : categoriesList) {
        dbUtils.select(sql, category).stream()
            .limit(50)
            .forEach(entry -> {
                int websiteId = (int) entry[0];
                String url = (String) entry[1];
                String categoryName = (String) entry[2];
                downloadURL(websiteId, url, categoryName, executorService);
            });
    }

sitesInCategory已替换为最多50个元素的流,然后您的代码将在每个条目上运行。

修改

关于评论。我已经进行了一些重组,你可以修改/实现我建议的方法的内容。

public void werk(Queue<Object[]> q, ExecutorService executorService) {
    executorService.submit(() -> {
        try {
            Object[] o = q.remove();
            try {
                String html = downloadHTML(o); // this takes one of your object arrays and returns the text of an html page

                insertIntoDB(html); // this is the code in the latter half of your downloadURL method
            }catch (/*narrow exception type indicating download failure*/Exception e) {
                werk(q, executorService);
            }
        }catch (NoSuchElementException e) {}
    });
}

^^^这种方法可以完成大部分工作。

for (String category : categoriesList) {
    Queue<Object[]> q = new ConcurrentLinkedQueue<>(dbUtils.select(sql, category));
    IntStream.range(0, 50).forEach(i -> werk(q, executorService));
}

^^^这是主

中的for循环

现在每个类别都会尝试下载50个页面,如果下载页面失败,它会继续下载并尝试下载另一个页面。这样,您将下载50页或尝试下载该类别中的所有页面。