简体   繁体   English

Java ExecutorService-为什么该程序保持运行?

[英]Java ExecutorService - why does this program keep running?

I'm trying to build something like a background task executor which terminates background tasks after a certain time if there's no answer (background tasks call webservices and they can time-out but I need to make sure they time out under a certain time) 我正在尝试构建类似后台任务执行程序的东西,如果没有答案,该代码将在一定时间后终止后台任务(后台任务调用webservices,它们可能会超时,但我需要确保它们在一定时间内超时)

So I have this as an experiment but if I run this the program does not terminate. 因此,我将其作为实验,但是如果运行此程序,程序不会终止。 I wonder if its because a background thread is still active maybe? 我想知道是否是因为后台线程仍然处于活动状态? How can I shut this down? 我该如何关闭?

public class Test {

public static class Task implements Callable<Object> {

    @Override
    public Object call() throws Exception {
        while(true) {}
    }

}

public static void main(String[] args) {
    try {
        Task t = new Task();
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.invokeAll(Arrays.asList(t), 5L, TimeUnit.SECONDS);
        executor.shutdown();
        System.out.println("DONE");
    } 
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

} }

The ExecutorService does not kill the running threads, and since threads are created as non-daemon, the JVM doesn't exit. 该ExecutorService的不杀死正在运行的线程,并且因为线程是作为非守护创建的,则JVM不会退出。

What happens is that when timeout expires, futures returned by invokeAll() are cancelled , which means that a flag is set on the future object and you get a CancellationException if you try to call future.get() . 发生的情况是,当超时到期时,invokeAll()返回的future被取消 ,这意味着在future对象上设置了一个标志,并且如果您尝试调用future.get() ,则会收到CancellationException However neither invokeAll(), nor shutdown() (or shutdownNow()) do anything to kill the thread. 然而,invokeAll()和shutdown()(或shutdownNow())都没有采取任何措施杀死线程。

Note that you cannot even kill threads yourself. 请注意,您甚至无法自己杀死线程。 All you can do is setting some application-specific flag or call Thread.interrupt() , but even that does not guarantee that the thread terminates . 您所能做的就是设置一些特定于应用程序的标志或调用Thread.interrupt() ,但这甚至不能保证线程终止

There is a great post by Winterbe on how executors work. 温特伯(Winterbe)在执行者的工作方式上有一篇很棒的文章。 This is an excerpt from his tutorial 这是他的教程的摘录

So basically executors always keep listening to the new tasks or callables/runnables and one way to shutdown the executor or stop the executor from listening is to interrupt whatever task it is executing. 因此,基本上执行者总是一直在监听新任务或可调用对象/可运行对象,而关闭执行者或停止执行者监听的一种方法是中断正在执行的任何任务。 One way to do is calling the future.get() which stops when the main thread , suspends it and makes sure that the current thread gets executed completely before handing over the resource to other thread 一种方法是调用future.get(),该方法在主线程停止时停止,将其挂起,并确保在将资源移交给其他线程之前完全执行当前线程。

You could probably have a higher number of threads and write your code to shutdown gracefully in the InterruptedException block 您可能拥有更多的线程,并在InterruptedException块中编写代码以正常关闭

Here is a sample code that I've written and tested: 这是我编写和测试的示例代码:

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecutorTest {

    public static void main(String[] args) {

        ExecutorService service = Executors.newWorkStealingPool(10);

        Callable<AccountClass> newInstance = () -> {
            TimeUnit.SECONDS.sleep(3);
            return getAcc(Thread.currentThread().getId());
        };

        // for now only one instance is added to the list
        // List<Callable<AccountClass>> callablesSingleList = Arrays.asList(newInstance);

        // adding multipleCallalbes
        List<Callable<AccountClass>> callablesMultipleList = Arrays.asList(
                () -> {
                    TimeUnit.SECONDS.sleep(3);
                    return getAcc(Thread.currentThread().getId());
                },
                () -> {
                    TimeUnit.SECONDS.sleep(3);
                    return getAcc(Thread.currentThread().getId());
                },
                () -> {
                    TimeUnit.SECONDS.sleep(3);
                    return getAcc(Thread.currentThread().getId());
                });

        try {
            service.invokeAll(callablesMultipleList).stream().map(future -> {
                AccountClass fuClass = null;
                try {
                    fuClass = future.get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                return fuClass;
            }).forEach(getValue -> {
                System.out.println("retunred value:" + getValue);
            });
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }

    }

    private static AccountClass getAcc(long itr) {
        // probably call DB for every new thread iterator
        System.out.println("getting the current thread" + itr);
        AccountClass ac = new AccountClass();
        ac.setId(itr);
        ac.setName("vv");
        ac.setRole("admin");
        System.out.println("sending the accnt class:" + ac);
        return ac;
    }
}

UPDATE: 更新:

Another way of shutting down the executor is using the service.shutDownNow() - > which shutdowns the program even if its the middle of execution. 关闭执行程序的另一种方法是使用service.shutDownNow()->,即使程序正在执行中,它也会关闭程序。 You could use awaitTermination method to specify if you feel that it might take a few minutes to complete execution and then probably shutdown the service 您可以使用awaitTermination方法来指定是否感到可能需要几分钟才能完成执行,然后可能会关闭服务

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ExecutorScheduleFixedRate {

    public static void main(String[] args) {

        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

        Runnable task = () -> {
            getAcc(33);
        };

        service.scheduleWithFixedDelay(task, 10, 5, TimeUnit.SECONDS);

        if (!service.isShutdown()) {
            List<Runnable> list2 = service.shutdownNow();
            System.out.println(list2);
            System.out.println("is shutdonw" + service.isShutdown());
            System.out.println("Do something after the thread execution");
        }

    }

    private static AccountClass getAcc(long itr) {
        // probably call DB for every new thread iterator
        System.out.println("getting the current thread" + itr);
        AccountClass ac = new AccountClass();
        ac.setId(itr);
        ac.setName("vv");
        ac.setRole("admin");
        System.out.println("sending the accnt class:" + ac);
        return ac;
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM