Java:杀死由ExecutorService作为Runnable启动的线程

时间:2015-04-26 22:18:47

标签: java multithreading runnable executorservice

我有一个系统,当它接到来自网络服务的电话时启动工作人员。工作程序由ExecutorService启动,正在启动的类实现Runnable。但是,如果工作人员超时,我实际上无法杀死工作人员,这会导致我的系统出现资源问题。

public class MyClass implements Runnable {

    public void internalCall() {
        logger.info("B-1");
        //Some business code which may take too long
        // <...>
        logger.info("B-2");
    }

    public void launch() {
        // Wrapper
        Callable<Object> callable = new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                internalCall();
                return null;
            }
        };

        // Submit
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Object> future = executor.submit(callable);

        try {
            // Wait
            future.get(1, TimeUnit.SECONDS);
        }
        catch (TimeoutException e) {
            logger.warn("Timeout");
        }
        finally {
            logger.info("A-1");
            executor.shutdownNow();
            future.cancel(true);
            logger.info("A-2");
        }
    }
}

如果工作人员超时,我希望得到以下日志消息:

INFO | B-1
WARN | Timeout
INFO | A-1
INFO | A-2

随后服务保持空闲状态,直到另一个工作者请求进入。但是,尽管在ExecutorService和Future上调用了shutdownNow()和cancel(),但工作人员继续:

INFO | B-1
WARN | Timeout
INFO | A-1
INFO | A-2
INFO | B-2

我环顾四周,还有许多关于杀死线程的其他类似问题,一般的共识是你不应该这样做。但是,这是一个可以扩展的类,意图覆盖internalCall() - 意味着我不能依靠internalCall来监控自己并检查Thread.isInterrupted()或类似的东西。

我想通过攻击furure或executor对象来强制从launch()方法中删除东西。

2 个答案:

答案 0 :(得分:4)

N.B。

  

如果某个线程没有响应Thread.interrupt怎么办?

     

在某些情况下,您可以使用特定于应用程序的技巧。例如,   如果线程正在等待已知套接字,则可以关闭套接字   导致线程立即返回。不幸的是,真的   不是任何一般的技术。 应该注意到   等待线程不响应的所有情况   Thread.interrupt,它也不会响应Thread.stop。这样   案例包括故意拒绝服务攻击和I / O操作   thread.stop和thread.interrupt无法正常工作。    - Java Thread Primitive Deprecation

所以我们学到了什么......在Java中,不要运行第三方代码,而这些代码实际上与主应用程序在同一执行中无法信任。即使Thread.stop确实有效,你仍然有很多其他的东西比没有检查中断状态的线程更糟糕(即代码调用System.exit(0))。

我建议您为第三方代码做的事情是:

  • 将第三方代码作为由您运行的评估语言运行,您可以控制其执行。一些例子是:规则语言,如Drools或无逻辑模板语言,如JMustache。

  • 在单独的执行中运行第三方代码,并使用您的操作系统终止进程和IPC,例如用于通信的套接字。

答案 1 :(得分:1)

首先,future.cancel(true)不会杀死正在运行的线程。它只是试图以礼貌的方式停止执行#34;。

它做什么它或多或少地发送中断信号,就像你在代码中某处调用yourthread.interrupt()一样。

仅当run()方法中的代码检查中断时,它才会停止处理。因此,在internalCall()内部,您需要立即检查是否通过调用Thread.interrupted()来中断线程并停止执行。中断还会通过抛出InterruptedExcepiton来停止sleep(),wait(),IO操作(这就是为什么那些方法抛出这样的异常)。

ExecutorService使用相同的机制,因此它将尝试中断正在运行的线程,但是,如果您的代码没有检查此类中断,则该线程将继续运行。

由于各种原因,不应该简单地杀死线程(检查java doc for thread.stop()方法以及为什么不推荐使用),以及&#34; universal&#34;通知线程可能它们应该停止工作的方式是中断信号。