这是继续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()
后,它运行了。
为什么遗嘱执行人不会终止?
答案 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)