简体   繁体   English

如何在提交给ExecutorService的Task中处理第三方Java代码,以防它具有无限循环

[英]How to handle third-party Java code in a Task submitted to ExecutorService in case it has an infinite loop

Let's say I have a third-party Java library called in a Task submitted to ExecutorService . 假设我在提交给ExecutorServiceTask调用了第三方Java库。

I trust the third-party library to not be malicious, but there is a rare chance that there are programming errors that can cause it to get stuck in an infinite loop, and if this is the case, I cannot fix it to address those rare occasions. 我相信第三方库不是恶意的,但是很少有机会出现编程错误,导致它陷入无限循环,如果是这种情况,我无法修复它以解决那些罕见的问题。场合。

What is the best way to handle this so that the application doesn't get stuck as well? 处理此问题的最佳方法是什么,以便应用程序也不会卡住? Is shutdownNow() good enough to handle this situation? shutdownNow()是否足以处理这种情况?

There's a related issue Stop an infinite loop in an ExecutorService task but this relies on the ability of the programmer to be cooperative and detect Thread.currentThread().isInterrupted() to stop processing, which I can't rely on. 有一个相关的问题在ExecutorService任务中停止无限循环,但这依赖于程序员协作并检测Thread.currentThread().isInterrupted()以停止处理的能力,这是我不能依赖的。

(In my case it's Jython code; in an early version of Jython the interpreter apparently didn't check Thread.currentThread().isInterrupted() , not sure what it does now... but my question is general for any 3rd-party Java code.) (在我的例子中它是Jython代码;在早期版本的Jython中,解释器显然没有检查Thread.currentThread().isInterrupted() ,不确定它现在做什么......但我的问题对于任何第三方都是一般的Java代码。)

If the task has an infinite loop that does not check for the thread interrupted status and does not use methods that throw InterruptedException s, it won't be stopped by shutdownNow() . 如果任务有一个无限循环,不检查线程中断状态,并且不使用抛出InterruptedException的方法,则shutdownNow()不会停止它。

Simple example that doesn't allow you program to finish: 不允许程序完成的简单示例:

public static void main(String[] args) throws Exception {
  ExecutorService e = Executors.newFixedThreadPool(1);
  e.submit(() -> { while (true); });
  e.shutdownNow();
  System.out.println("Main is finished but the app keeps running");
}

One way would be to run the thread as a daemon: 一种方法是将线程作为守护进程运行:

public static void main(String[] args) throws Exception {
  ExecutorService e = Executors.newFixedThreadPool(1, r -> {
      Thread t = new Thread(r);
      t.setDaemon(true);
      return t;
    });
  e.submit(() -> { while (true); });
  e.shutdownNow();
  System.out.println("Main is finished and the app can exit");
}

Following my correct reading of the question I put together this set of classes. 在我正确阅读了这个问题后,我将这组课程放在一起。 Relatively simple: One Runnable that connects to a socket sending input and retrieving output from a secondary jvm that invokes the erratic library. 相对简单:一个Runnable连接到套接字发送输入并从辅助jvm检索调用不稳定库的输出。

If after 3 tries no response has been received the secondary jvm is killed. 如果在3次尝试后没有收到任何响应,则杀死次要的jvm。 But it could be relaunched. 但它可以重新启动。 The secondary jvm has an exit hook to close down sockets. 辅助jvm有一个关闭插座的出口钩。

class SafetyValve implements Runnable{
    PrintWriter out;
    BufferedReader in;
    Socket s = null;

    AtomicBoolean flag;

    SafetyValve(AtomicBoolean b){
        flag = b;
    }
    @Override
    public void run() {
        try {
            s = new Socket("localhost", 9000);
            out = new PrintWriter(s.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));

            while (!Thread.currentThread().isInterrupted()){
                flag.set(false);
                out.print(0);
                out.flush();
                System.out.print(in.read());
                flag.set(true);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        finally{
            try {
                s.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

Main/Controller class. 主/控制器类。 It uses a Thread class for control 它使用Thread类进行控制

public class Switch {
    public static void main(String[] args) {
        try {
            AtomicBoolean flag = new AtomicBoolean(false);
            int counter = 0;
            ProcessBuilder pb = ...
            pb.directory(,,,);
            Process p = pb.start();
            SafetyValve sv = new SafetyValve(flag);
            Thread t = new Thread(sv);
            t.start();
            while(t.getState() != Thread.State.RUNNABLE){
                Thread.sleep(10);
            }
            while(true){
                if (flag.get() == false){
                    if (++counter == 3){
                        while(t.getState() != Thread.State.TERMINATED){
                            p.destroyForcibly();
                            t.interrupt();
                            Thread.sleep(10);
                        }
                        break;
                    }
                }
                else
                    counter = 0;
                Thread.sleep(100);
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

The secondary jvm has an standard server socket implementation: 辅助jvm具有标准服务器套接字实现:

class UnYielding{
    int i = 0;

    int returnInt(){
        i++;
        if (i > 2)
            while(true);
        return i;
    }
}

class Hook extends Thread{

    RunWild rw;

    Hook(RunWild wr){
        rw = wr;
    }

    public void run() {
        try {
            System.out.println("exit...");
            System.out.flush();
            rw.socket.close();
            rw.server.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

public class RunWild {

    ServerSocket server;
    Socket socket;

    RunWild(){
        Runtime.getRuntime().addShutdownHook(new Hook(this));
    }

    public static void main(String[] args){
        UnYielding u;
        int i;
        PrintWriter out;
        BufferedReader in;
        RunWild rw = new RunWild();

        try {
            rw.server = new ServerSocket(9000);
            rw.socket = rw.server.accept();
            out = new PrintWriter(rw.socket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(rw.socket.getInputStream()));
            u = new UnYielding();
            while ((i = in.read()) != -1){
                out.print(u.returnInt());
                out.flush();
                Thread.sleep(10);
                System.out.print("waiting...");
                System.out.flush();
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

I have tested this against 1.8 on OS X it works as expected. 我已经在OS X上对1.8进行了测试,它按预期工作。 If this unstable classes are needed this is one way of doing it 如果需要这个不稳定的类,这是一种方法

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

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