2017-02-08 2 views
3

Предположим, у меня есть сторонняя библиотека Java, вызванная в Task, представленная на ExecutorService.Как обрабатывать сторонний код Java в задаче, представленной ExecutorService, в случае, если она имеет бесконечный цикл

Я доверяю сторонней библиотеке, чтобы она не была злонамеренной, но есть редкий шанс, что есть ошибки программирования, которые могут заставить ее застревать в бесконечном цикле, и если это так, я не могу ее исправить для решения этих редких случаев.

Каков наилучший способ справиться с этим, чтобы приложение не застревало? Есть shutdownNow() достаточно хорошо, чтобы справиться с этой ситуацией?

Существует связанная с этим проблема Stop an infinite loop in an ExecutorService task, но это зависит от способности кооператора работать и обнаруживать Thread.currentThread().isInterrupted(), чтобы остановить обработку, на которую я не могу положиться.

(В моем случае это Jython код;. В ранней версии Jython the interpreter apparently didn't check Thread.currentThread().isInterrupted(), не уверен, что он делает сейчас ... но мой вопрос вообще для любой третьих сторон Java коды)

+0

hmmm .. может быть дубликат http://stackoverflow.com/questions/10853305/how-to-stop-long-duration-execution-task- например, бесконечный цикл-выполнение-внутри, но я не уверен. –

+0

«положить в состояние проверки цикла при прерывании» - я не могу, если такой цикл существует, он находится в стороннем коде. –

+1

@efekctive Это не имеет большого значения - если вы завершите метод в runnable: 'run() {run3rdParty(); } 'и' run3rdParty' никогда не возвращается, вы не можете сделать много: если вы вставляете проверку прерывания, она не будет выполнена. – assylias

ответ

1

Если задача имеет бесконечный цикл, который не проверяет состояние прерывания потока и не использует методы, которые выкидывают InterruptedException s, он не будет остановлен на shutdownNow().

Простой пример, который не позволяет программе закончить:

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

Одним из способов было бы запустить поток как демон:

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"); 
} 
+0

hmm, это работает, если жизненный цикл JVM совпадает с жизненным циклом вызывающего абонента, но если это плагин, а JVM длится намного дольше, это будет поток демона зомби. –

+0

@JasonS Пока приложение работает, задача будет продолжать работать, если вы не можете его прерывать. – assylias

+0

grrrr. Удивлен, что он работает именно так на Java, учитывая все внимание, которое они уделяют привилегии безопасности. –

1

После моего правильного чтения вопроса я соединить этот набор классов. Относительно просто: один Runnable, который подключается к сокету, отправляющему входные данные, и извлекает выходные данные из вторичного jvm, который вызывает беспорядочную библиотеку.

Если после 3 попыток ответа не получено, вторичный jvm будет убит. Но его можно было возобновить. Вторичный 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(); 
      } 
     } 
    } 

} 

Главный/Контроллер класс. Он использует класс 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(); 
     } 
    } 
} 

Вторичный 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(); 
     } 

    } 

} 

Я испытал это на 1.8 на OS X это работает, как ожидалось. Если это неустойчивые классы необходимы, это один из способов сделать это.