我有一个处理文件目录的任务,如果出现任何问题,则需要抛出IOException.我还需要它更快,所以我将完成的工作分成多个线程并等待它们终止.看起来像这样:
//Needs to throw IOException so the rest of the framework handles it properly.
public void process(File directory) throws IOException {
ExecutorService executorService =
new ThreadPoolExecutor(16, 16, Long.MAX_VALUE, TimeUnit.NANOSECONDS,
new LinkedBlockingQueue());
//Convenience class to walk over relevant file types.
Source source = new SourceImpl(directory);
while (source.hasNext()) {
File file = source.next();
executorService.execute(new Worker(file));
}
try {
executorService.shutdown();
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
executorService.shutdownNow();
throw new IOException("Worker thread had a problem!");
}
}
虽然Worker线程基本上是:
private class Worker implements Runnable {
private final File file;
public Worker(File file) { this.file = file; }
@Override
public void run() {
try {
//Do work
} catch (IOException e) {
Thread.currentThread().interrupt();
}
}
}
期望的行为是,如果任何Worker具有IOException,则使生成线程知道该异常,并且可以依次抛出其自己的IOException.这是我想到的允许Worker线程发出错误信号的最佳方法,但是我仍然不确定我是否将其设置正确.
那么,首先,这会符合我的期望吗?如果一个辅助线程在run()中有错误,将调用Thread.currentThread().interrupt();.是否导致引发InterruptedException,使其被阻塞的executorService.awaitTermination(Long.MAX_VALUE,TimeUnit.NANOSECONDS)捕获?
其次,如果正在运行的Worker在所有线程都已排队之前调用其中断,将会发生什么情况;在阻止try / catch块之前?
最后(也是最重要的一点),还有什么更优雅的方法可以实现我的目标?我希望所有无数子线程都执行到完成,或者直到其中任何一个出现错误为止,这时我想在生成线程中处理它(通过有效地使整个目录失败).
解
根据答案,这是我最终使用的实现.它很好地处理了我的异步需求,并且在IOExceptions上干净且相对快速地失败了.
public void process(File directory) throws IOException {
//Set up a thread pool of 16 to do work.
ExecutorService executorService = Executors.newFixedThreadPool(16);
//Arbitrary file source.
Source source = new SourceImpl(directory);
//List to hold references to all worker threads.
ArrayList> filesToWork =
new ArrayList>();
//Service to manage the running of the threads.
ExecutorCompletionService ecs =
new ExecutorCompletionService(executorService);
//Queue up all of the file worker threads.
while (source.hasNext())
filesToWork.add(new Worker(file));
//Store the potential results of each worker thread.
int n = filesToWork.size();
ArrayList> futures =
new ArrayList>(n);
//Prepare to return an arbitrary worker's exception.
IOException exception = null;
try {
//Add all workers to the ECS and Future collection.
for (Callable callable : filesToWork)
futures.add(ecs.submit(callable));
for (int i = 0; i < n; i++) {
try {
//Get each result as it's available, sometimes blocking.
IOException e = ecs.take().get();
//Stop if an exception is returned.
if (e != null) {
exception = e;
break;
}
//Also catch our own exceptions.
} catch (InterruptedException e) {
exception = new IOException(e);
break;
} catch (ExecutionException e) {
exception = new IOException(e);
break;
}
}
} finally {
//Stop any pending tasks if we broke early.
for (Future f : futures)
f.cancel(true);
//And kill all of the threads.
executorService.shutdownNow();
}
//If anything went wrong, it was preserved. Throw it now.
if (exception != null)
throw exception;
}
和
//Does work, and returns (not throws) an IOException object on error.
private class Worker implements Callable {
private final File file;
public Worker(File file) { this.file = file; }
@Override
public IOException call() {
try {
//Do work
} catch (IOException e) {
return e;
}
return null;
}
}