【问题标题】:How to stop an ExecutorService completely?如何完全停止 ExecutorService?
【发布时间】:2014-04-08 23:39:48
【问题描述】:

我在 SO 上问了一个问题 here 并得到了解决方案 there。但是在运行该解决方案之后,它似乎无法按预期工作。

换句话说,我正在遍历一堆 Java 文件并逐个分析它们。我的分析算法可能需要 1 秒到 5-10 小时,可能。但如果分析在 10 分钟内还没有完成,我想完成分析。程序长时间运行也会产生内存不足的异常。

这是我正在使用的代码。

for循环:

for (File file : files)
{
String fileName = file.getName();
String filePath = file.getAbsolutePath();

ExecutorService executor = Executors.newSingleThreadExecutor();
Task ts = new Task(filePath);
Future<String> future = executor.submit(ts);
try {
    System.out.println("@@ Started.. " + fileName );
    System.out.println(future.get(10, TimeUnit.MINUTES));
    System.out.println("@@ Finished!" + fileName);
} catch (TimeoutException e) {
    System.out.println("@@ Terminated!" + fileName);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (ExecutionException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
finally{
    shutdownAndAwaitTermination(executor, future);
    future.cancel(true);
}
}

shutdownAndAwaitTermination(,) 方法:

// http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html
public static void  shutdownAndAwaitTermination(ExecutorService pool, Future<String> futureObj) {
       pool.shutdown(); 
       try {
         if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
           pool.shutdownNow(); 
           if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           {
               System.err.println("Pool did not terminate");
           }
         }
       } catch (InterruptedException ie) {
         pool.shutdownNow();
         Thread.currentThread().interrupt();
       }
     }

}

任务类:

class Task implements Callable<String> {

    String fName = "";
    Task(String file)
    {
        fName = file;
    }
    @Override
    public String call() throws Exception {

        while(!Thread.currentThread().isInterrupted())
        {
    // Long running program on file fName.          
       }
        return "Ready!";
    }
}

我收到以下异常:

@@ Started.. file1.java
@@ Terminated!file1.java
Pool did not terminate
@@ Started.. file2.java
java.util.concurrent.ExecutionException: java.util.ConcurrentModificationException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:262)
    at java.util.concurrent.FutureTask.get(FutureTask.java:119)
    at controller.Controller.main(Controller.java:75)
Caused by: java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
    at java.util.ArrayList$Itr.next(ArrayList.java:791)
    at iterator.CFGIterator.combineCFGs(CFGIterator.java:137)
    at iterator.CFGIterator.setOneCFG(CFGIterator.java:92)
    at iterator.CFGIterator.<init>(CFGIterator.java:41)
    at cfg.ClassObj.iterator(ClassObj.java:47)
    at taintanalyzer.TaintAnalyzer.analyze(TaintAnalyzer.java:196)
    at cfg.ClassObj.accept(ClassObj.java:65)
    at taintanalyzer.TaintAnalyzer.analyze(TaintAnalyzer.java:348)
    at controller.Task.call(Controller.java:140)
    at controller.Task.call(Controller.java:1)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:722)

【问题讨论】:

  • 您似乎在// Long running program 中有一个ConcurrentModificationException
  • 当我在 file2 上单独运行时,它不会给我这个异常。我猜,file1 进程没有被终止/杀死,并且仍在使用资源。
  • 除非您展示它在做什么,否则我们无能为力(最好是一个小的可重现示例。)
  • 它基本上是一种在该文件上运行的静态污点分析算法。不知道我可以在这里粘贴多少代码。而且它是一个研究工具,所以不能分享太多逻辑。
  • 如果您发布更多堆栈跟踪,或者查看它,它会告诉您它发生在哪里。否则,你能证明它与未完成的ExecutorService有什么关系吗?

标签: java multithreading executorservice


【解决方案1】:

你的问题在这里:

for (File file : files) { //... }

我猜你使用ArrayList&lt;File&gt; 来保存许多对象文件。但是,数据结构ArrayList 不是线程安全的。

ArrayList Java Doc

请注意,此实现不同步。如果多个线程同时访问一个 ArrayList 实例,并且至少有一个线程在结构上修改了列表,则必须外部同步

您可以尝试使用CopyOnWriteArrayList 代替ArrayList 来处理大型列表并获得性能。

编辑:

如果你想看到第一个文件运行结束并坚持使用awaiTermination,你需要将超时参数设置得足够大。

   pool.shutdown(); 
   try {
     // Wait longer, example 6000 seconds for existing task to terminate
     if (!pool.awaitTermination(6000, TimeUnit.SECONDS)) {
       pool.shutdownNow(); 
       if (!pool.awaitTermination(6000, TimeUnit.SECONDS))
       {
           System.err.println("Pool did not terminate");
       }
     }
   } catch (InterruptedException ie) {
     pool.shutdownNow();
     Thread.currentThread().interrupt();
   }

【讨论】:

  • 发布的代码中没有任何内容表明它被修改了。
  • 是的。诚然。但对我来说问题是如何停止仍在后端执行的第一个线程。
  • 我的意思是,当我们调用 shutdownNow() 和 future.cancel() 时,该进程应该释放所有资源(ArrayList 等)并且当它在 file2 上运行时,它应该重新开始.当进程单独运行时,它工作正常。但依次导致并发错误,因为前一个进程还没有释放所有资源。
猜你喜欢
  • 2015-02-24
  • 2014-02-08
  • 2013-10-22
  • 1970-01-01
  • 1970-01-01
  • 2016-08-31
  • 2013-12-01
  • 2011-08-22
  • 2020-12-07
相关资源
最近更新 更多