终止并向执行者提交callables

时间:2015-12-02 21:59:30

标签: java multithreading future executorservice callable

这是继续earlier post,作为我的任务的一部分,我正在尝试使用callables从URL下载文件,并且每当发生异常时我都会尝试重新提交相同的callable以获得最大数量时间。

问题是,使用当前的方法,我的程序在快乐的一天场景中完成所有的callables后不会终止,它会一直运行(可能是因为我使用非守护程序线程?它不会终止经过一段时间后?)。

另外我相信当前的设计会阻止重新提交失败的callable,因为我正在调用executor.shutdown(),因此每当调用失败时,执行程序将阻止向执行队列添加新的callable。

任何想法如何克服这个?

public class DownloadManager {

int allocatedMemory;
private final int MAX_FAILURES = 5;
private ExecutorService executor;
private CompletionService<Status> completionService;
private HashMap<String, Integer> failuresPerDownload;
private HashMap<Future<Status>, DownloadWorker> URLDownloadFuturevsDownloadWorker;

public DownloadManager() {
    allocatedMemory = 0;
    executor = Executors.newWorkStealingPool();
    completionService = new ExecutorCompletionService<Status>(executor);
    URLDownloadFuturevsDownloadWorker = new HashMap<Future<Status>, DownloadWorker>();
    failuresPerDownload = new HashMap<String, Integer>();
}

public ArrayList<Status> downloadURLs(String[] urls, int memorySize) throws Exception {
    validateURLs(urls);
    for (String url : urls) {
        failuresPerDownload.put(url, 0);
    }
    ArrayList<Status> allDownloadsStatus = new ArrayList<Status>();
    allocatedMemory = memorySize / urls.length;
    for (String url : urls) {
        DownloadWorker URLDownloader = new DownloadWorker(url, allocatedMemory);
        Future<Status> downloadStatusFuture = completionService.submit(URLDownloader);
        URLDownloadFuturevsDownloadWorker.put(downloadStatusFuture, URLDownloader);
    }
    executor.shutdown();
    Future<Status> downloadQueueHead = null;
    while (!executor.isTerminated()) {
        downloadQueueHead = completionService.take();
        try {
            Status downloadStatus = downloadQueueHead.get();
            if (downloadStatus.downloadSucceeded()) {
                allDownloadsStatus.add(downloadStatus);
                System.out.println(downloadStatus);
            } else {
                handleDownloadFailure(allDownloadsStatus, downloadStatus.getUrl());

            }
        } catch (Exception e) {
            String URL = URLDownloadFuturevsDownloadWorker.get(downloadQueueHead).getAssignedURL();
            handleDownloadFailure(allDownloadsStatus, URL);
        }
    }
    return allDownloadsStatus;
}

private void handleDownloadFailure(ArrayList<Status> allDownloadsStatus, String URL) {
    int failuresPerURL = failuresPerDownload.get(URL);
    failuresPerURL++;
    if (failuresPerURL < MAX_FAILURES) {
        failuresPerDownload.put(URL, failuresPerURL);
        // resubmit the same job
        DownloadWorker downloadJob = URLDownloadFuturevsDownloadWorker.get(URL);
        completionService.submit(downloadJob);
    } else {
        Status failedDownloadStatus = new Status(URL, false);
        allDownloadsStatus.add(failedDownloadStatus);
        System.out.println(failedDownloadStatus);
    }
  }                  
}

更新:在我将while循环的条件更改为计数器而不是!executor.isTerminated()后,它运行了。 为什么遗嘱执行人不会终止?

2 个答案:

答案 0 :(得分:0)

在完成所有工作后,您需要调用ExecutorService.shutdown()awaitTermination()来终止线程。

或者,您可以在构造ThreadFactory时提供自己的ExecutorService,并将所有线程标记为守护进程,以便在主线程退出后不会让您的进程保持活动状态。

答案 1 :(得分:0)

ExecutorCompletionService javadoc中,我们看到了示例

CompletionService<Result> ecs
         = new ExecutorCompletionService<Result>(e);
     List<Future<Result>> futures
         = new ArrayList<Future<Result>>(n);
try {
...
} finally {
         for (Future<Result> f : futures)
             f.cancel(true);
     }

因此,当您需要停止ExecutorCompletionService时,尝试使用所有Future调用cancel(true)