【问题标题】:java multi threading executor never shutdown threadsjava多线程执行器从不关闭线程
【发布时间】:2019-11-17 23:13:37
【问题描述】:

有人可以看看下面这个程序吗?

小进程运行正常,大进程完成后不退出程序。

注意:如果是小查询,大约50条记录(检索和更新),程序正在正常退出......

这个程序的目的是从数据库中获取数据,去云端读取JSON,验证数据并用结果更新数据库中的记录。

public class ThreadLauncher
{

  public static void main(String args[])
   {
    final ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // or hardcode a number



    List<Future<Runnable>> futures = new ArrayList<Future<Runnable>>();

    for (int n = 0; n < 10; n++)
    {
        Future f = service.submit(new Task(n));
        futures.add(f);
    }

    // wait for all tasks to complete before continuing
    for (Future<Runnable> f : futures)
    {
        try {

            f.get();
            //shut down the executor service so that this thread can exit

        } catch (InterruptedException e) {
            System.out.println("Exiting with InterruptedException : " + e.getMessage());
            e.printStackTrace();
        } catch (ExecutionException e) {
            System.out.println("Exiting with ExecutionException : " + e.getMessage());
            e.printStackTrace();
        }
    }
    service.shutdownNow();


    System.out.println("Exiting normally...");

}
}

 final class Task
    implements Runnable
{
private int loopCounter;
private int totalLoops = 5;


public Task(int counter)
{
    this.loopCounter = counter;
}

@Override
public void run()
 {
    try { 
        GCPJSON.getInstance().getGCPDataFromJSON(PRODDataAccess.getInstance().getDataToProcess(loopCounter,totalLoops));

        System.out.println("Task ID : " + this.loopCounter + " performed by " + 
     Thread.currentThread().getName());
    } catch (Exception e) {

        e.printStackTrace();
    }

 }
}

【问题讨论】:

  • 您需要进一步了解线程是如何“正常”终止的其中一件事。在Java中,你不能停止一个正在运行的线程,你所能做的就是“中断”线程,并希望任何正在运行的任务都支持中断。
  • JavaDocs for shutdownNow 甚至这么说 - “除了尽力停止处理正在执行的任务之外,没有任何保证。例如,典型的实现将通过 Thread.interrupt() 取消,因此任何无法响应中断的任务都可能永远不会终止。”
  • 如前所述,您需要更好地理解 JVM 中的整体“线程”。在您的示例中,shutdownNow() 也不会添加任何值,因为您已经通过调用 future.get() 阻塞了主线程,即使这些任务没有实现 Callable 接口,这意味着它们不会返回任何内容。仍然不清楚您要实现什么目标
  • 感谢您的反馈。完成该过程后,我需要干净地退出程序。您能否更新代码以添加 Callable ?或者给我举一个很好的例子?

标签: java multithreading executorservice


【解决方案1】:

这是我更新的代码。我已将其从 Future 更改为 FutureTask 并添加了几行项目。我希望所有这 10 个任务并行运行。

List<FutureTask<Runnable>> futures = new ArrayList<FutureTask<Runnable>>();

    for (int n = 0; n < 10; n++)
    {
        FutureTask f = (FutureTask) service.submit(new Task(n));
        futures.add(f);
    }

    // wait for all tasks to complete before continuing
   // for (FutureTask<Runnable> f : futures)
    for (int i=0; i< futures.size(); i++)
    {
       FutureTask f = (FutureTask)futures.get(i) ;
        //System.out.println("Number of futureTasks: " + i);
        try {


            if(!f.isDone()){
                //wait indefinitely for future task to complete
                f.get();
                //System.out.println("FutureTask output="+f.get());
            }else{
                System.out.println("Task number :" + i + "Done.");
            }

        } catch (InterruptedException | ExecutionException e) {
            System.out.println("Exiting with InterruptedException : " + e.getMessage());
            e.printStackTrace();
        }
    }
 //If we come out from the loop, we must have completed all the tasks. e.e. In above case , 10 tasks ( 10 loop submites)
    try {
        if (!service.awaitTermination(10000000, TimeUnit.MICROSECONDS)) {
            System.out.println("Exiting normally...");
            service.shutdownNow();
            System.exit(0);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if(!service.isShutdown()){
        System.exit(0);
    }

【讨论】:

    【解决方案2】:

    这是因为当您在 executorService 上调用 shutdownshutdownNow 时,它只会尝试停止活动线程,并且会根据 Java 文档返回活动任务列表:

    尝试停止所有正在执行的任务,停止处理 等待任务,并返回正在等待的任务列表 执行。

    此方法不等待主动执行的任务终止。 使用 {awaitTermination} 来做到这一点。

    如文档所述您需要调用 awaitTermination 以确保每个线程都已完成,否则此方法将在超时结束时杀死它们。

    更新:

    如果您不知道时间估计,您可以添加以下行以确保所有线程都已成功完成。

    int filesCount = getFileCount();//you know the files count, right?
    AtomicInteger finishedFiles = new AtomicInteger(0);
    ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
    for (int i = 0; i < threadCount; i++)
        executorService.submit(() -> {
            //do you work
            //at the end of each file process
            finishedFiles.incrementAndGet();
        }
    while (finishedFiles.get() < filesCount) { //let's wait until all files have been processed 
        Thread.sleep(100);
    }
    executorService.shutdown();
    executorService.awaitTermination(1, TimeUnit.MINUTES);//anyway they already should have finished 
    

    【讨论】:

    • 我不能使用 awaitTerminaion,因为我不确定需要多长时间。我将处理数百万条记录。我希望程序弄清楚并干净地退出。
    • 这是唯一的解决方案,你必须对过程有一些估计,取上层键,例如如果你预计1个文件最多需要10分钟才能完成,写成awaitTermination(numberOfFiles*10, TimeUnit.MINUTES)跨度>
    • @user3067524 我添加了代码示例,以防您无法估计处理时间
    • @user3067524 在你的问题中,你说你的问题是程序没有完成。现在你说你不想等到它完成。那么它是什么?
    猜你喜欢
    • 2016-10-25
    • 2013-02-27
    • 1970-01-01
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 2017-06-19
    相关资源
    最近更新 更多