Java ExecutorService - 为什么这个程序继续运行?

时间:2015-08-30 22:47:50

标签: java multithreading executorservice

我正在尝试构建类似后台任务执行器的东西,如果没有答案,它会在一定时间后终止后台任务(后台任务调用webservices并且它们可以超时但是我需要确保它们在某个时间超时时间)

所以我把它作为一个实验,但如果我运行它,程序不会终止。我想知道它是否因为后台线程仍然有效?我怎么能把它关闭呢?

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();
    }
}

}

2 个答案:

答案 0 :(得分:4)

ExecutorService不会 kill 正在运行的线程,并且由于线程是作为非守护进程创建的,因此JVM不会退出。

当超时到期时,invokeAll()返回的期货被取消,这意味着在未来的对象上设置了一个标志,如果你尝试,你得到一个CancellationException致电future.get()。但是,invokeAll()和shutdown()(或shutdownNow())都不会做任何事情来杀死线程。

请注意,您自己甚至无法杀死线程。您所能做的只是设置一些特定于应用程序的标记或致电Thread.interrupt(),但即便如此也不能保证the thread terminates

答案 1 :(得分:1)

Winterbe上有一篇很棒的文章,介绍了执行者的工作方式。这是他的教程的摘录

因此,基本上,执行者总是一直在监听新任务或可调用对象/可运行对象,而关闭执行者或停止执行者监听的一种方法是中断正在执行的任何任务。一种方法是调用future.get(),该方法在主线程停止时停止,将其挂起,并确保在将资源移交给其他线程之前完全执行当前线程

您可能拥有更多的线程,并在InterruptedException块中编写代码以正常关闭

这是我编写和测试的示例代码:

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;
    }
}

更新:

关闭执行程序的另一种方法是使用service.shutDownNow()->,即使程序在执行的中间,它也会关闭程序。您可以使用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;
    }

}