Java 创建自中断服务
我有一个任务要处理一个文件目录,如果出现任何问题,需要抛出一个IOException。我还需要它运行得更快,所以我将完成的工作拆分为多个线程,并等待它们的终止。它看起来像这样:Java 创建自中断服务,java,multithreading,executorservice,runnable,interrupted-exception,Java,Multithreading,Executorservice,Runnable,Interrupted Exception,我有一个任务要处理一个文件目录,如果出现任何问题,需要抛出一个IOException。我还需要它运行得更快,所以我将完成的工作拆分为多个线程,并等待它们的终止。它看起来像这样: //Needs to throw IOException so the rest of the framework handles it properly. public void process(File directory) throws IOException { ExecutorService execu
//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<Runnable>());
//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!");
}
}
所需的行为是,如果任何工作线程具有IOException,那么生成线程就会知道它,并反过来抛出自己的IOException。这是我能想到的允许工作线程发出错误信号的最佳方式,但我仍然不确定我是否正确设置了它
那么,首先,这能满足我的期望吗?如果工作线程在run()中有错误,将调用thread.currentThread().interrupt()代码>导致抛出InterruptedException,使其被阻塞executorService.awaitTermination捕获(Long.MAX_值,时间单位为纳秒)代码>
其次,如果一个正在运行的工作线程在所有线程排队之前调用其中断,将会发生什么情况;在阻塞try/catch阻塞之前
最后(也是最重要的),有没有更优雅的方式来实现我的目标?我想让所有无数的子线程都执行,直到完成或其中任何一个出现错误为止,此时我希望在生成线程中处理它(通过有效地使整个目录失败)
解决方案
根据答案,下面是我最终使用的实现。它很好地处理了我的异步需求,并在IOException上以干净和相对较快的速度失败
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<Callable<IOException>> filesToWork =
new ArrayList<Callable<IOException>>();
//Service to manage the running of the threads.
ExecutorCompletionService<IOException> ecs =
new ExecutorCompletionService<IOException>(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<Future<IOException>> futures =
new ArrayList<Future<IOException>>(n);
//Prepare to return an arbitrary worker's exception.
IOException exception = null;
try {
//Add all workers to the ECS and Future collection.
for (Callable<IOException> 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<IOException> 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;
}
public void进程(文件目录)引发IOException{
//设置16个线程池来执行工作。
ExecutorService ExecutorService=Executors.newFixedThreadPool(16);
//任意文件源。
Source Source=新的SourceImpl(目录);
//保存对所有工作线程的引用的列表。
ArrayList文件工作=
新的ArrayList();
//服务来管理线程的运行。
ExecutorCompletionService ecs=
新的ExecutorCompletionService(executorService);
//将所有文件工作线程排队。
while(source.hasNext())
添加(新工作者(文件));
//存储每个辅助线程的潜在结果。
int n=filesToWork.size();
ArrayList期货=
新ArrayList(n);
//准备返回任意工作进程的异常。
IOException=null;
试一试{
//将所有工人添加到ECS和未来集合。
for(可调用可调用:filesToWork)
期货.add(ecs.submit(callable));
对于(int i=0;i
及
//正常工作,并在出错时返回(而不是抛出)IOException对象。
私有类工作程序实现了可调用{
私人最终文件;
公共工作者(文件){this.File=File;}
@凌驾
公共IOException调用(){
试一试{
//工作
}捕获(IOE异常){
返回e;
}
返回null;
}
}
一如既往,线程之间的最佳通信是队列。让每个工作线程发送一条消息,描述其执行是如何完成的,并让生成线程从队列中读取。此外,由于生成线程知道它生成了多少个工作线程,因此它可以只计算消息以知道所有工作线程何时完成,而不依赖于池关闭。这样调用中断()
不会影响主线程
您应该做的是将您的工作人员设置为可调用的,而不是可运行的,并允许故障异常离开调用()方法。然后,使用命令执行所有工作人员。这将允许您确定每个任务的状态,并在其中一个任务失败时在主线程中采取操作。使执行的工作人员可调用,您可以执行以下操作:
public void process(File directory) throws IOException {
ExecutorService executorService = new ThreadPoolExecutor(16, 16,
Long.MAX_VALUE, TimeUnit.NANOSECONDS,
new LinkedBlockingQueue<Runnable>());
// Convenience class to walk over relevant file types.
List<Future<Void>> futures = new ArrayList<Future<Void>>();
Source source = new SourceImpl(directory);
while (source.hasNext()) {
File file = source.next();
futures.add(executorService.submit(new Worker(file)));
}
try {
for (Future<Void> future : futures) {
future.get();
}
} catch (ExecutionException e) {
throw new IOException("Worker thread had a problem!", e.getCause());
} catch (InterruptedException e) {
throw new IOException("Worker thread had a problem!", e);
} finally {
executorService.shutdown();
}
}
public void进程(文件目录)引发IOException{
ExecutorService ExecutorService=新线程池Executor(16,16,
Long.MAX_值,时间单位为纳秒,
新建LinkedBlockingQueue());
//方便类浏览相关文件类型。
列表期货=新的ArrayList();
Source Source=新的SourceImpl(目录);
while(source.hasNext()){
File=source.next();
futures.add(executorService.submit(新员工(文件));
}
试一试{
for(未来:未来){
future.get();
}
}捕获(执行例外){
抛出新IOException(“工作线程有问题!”,e.getCause();
}捕捉(中断异常e){
抛出新IOException(“工作线程有问题!”,e);
}最后{
executorService.shutdown();
}
}
Thread.currentThread().interrupt();仅中断当前正在运行的线程,在本例中,该线程就是工作线程本身。所有其他正在运行的线程都不受此中断的影响。并且您不能依赖InterruptedException,因为它可能会被抛出,也可能不会被抛出。和e
//Does work, and returns (not throws) an IOException object on error.
private class Worker implements Callable<IOException> {
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;
}
}
public void process(File directory) throws IOException {
ExecutorService executorService = new ThreadPoolExecutor(16, 16,
Long.MAX_VALUE, TimeUnit.NANOSECONDS,
new LinkedBlockingQueue<Runnable>());
// Convenience class to walk over relevant file types.
List<Future<Void>> futures = new ArrayList<Future<Void>>();
Source source = new SourceImpl(directory);
while (source.hasNext()) {
File file = source.next();
futures.add(executorService.submit(new Worker(file)));
}
try {
for (Future<Void> future : futures) {
future.get();
}
} catch (ExecutionException e) {
throw new IOException("Worker thread had a problem!", e.getCause());
} catch (InterruptedException e) {
throw new IOException("Worker thread had a problem!", e);
} finally {
executorService.shutdown();
}
}