2013-06-21 12 views
13

Ho un percorso di esecuzione sincrono che deve essere completato o scaduto entro un determinato periodo di tempo. Diciamo che ho una classe con il metodo main() in cui invoco i metodi A() che inviano chiamate B() e che inviano chiamate C() di classi uguali o diverse ..... tutte sincrone senza utilizzare una risorsa esterna come database, webservice o file system (dove ognuno di essi potrebbe essere scaduto indipendentemente usando un TxManager o le rispettive API di timeout). Quindi è più come una CPU o un calcolo intensivo della memoria. Come posso programmare il timeout in Java?Come implementare i timeout del metodo sincrono in Java?

Ho visto TimerTask ma più di rendere il flusso asincrono e per pianificare le attività. Qualche altro suggerimento?

+0

'ExecutorService' con' Callable's può essere !!! – NINCOMPOOP

+0

È un compito iterativo? Potresti controllare ogni iterazione se il tuo timeout è già stato raggiunto? – keuleJ

+0

Stavo considerando una soluzione generica piuttosto che renderla specifica per l'implementazione ... perché potrebbe esserci un singolo calcolo che potrebbe richiedere abbastanza tempo per essere completato. – Smitesh

risposta

25

Si dovrebbe usare ExecutorService per farlo

ExecutorService executor = Executors.newSingleThreadExecutor(); 
Future<String> future = executor.submit(new Callable() { 

    public String call() throws Exception { 
     //do operations you want 
     return "OK"; 
    } 
}); 
try { 
    System.out.println(future.get(2, TimeUnit.SECONDS)); //timeout is in 2 seconds 
} catch (TimeoutException e) { 
    System.err.println("Timeout"); 
} 
executor.shutdownNow(); 
+0

Questo arresta effettivamente il Callable o semplicemente il timeout del get()? – keuleJ

+0

È necessario "timeout" per ottenere, è necessario interrompere il futuro tramite il metodo "cancel (true)" o chiamare executor.shutdownNow() –

2

È possibile eseguire un thread parallelo che attenderà il timeout specificato e interromperà il thread corrente, quindi eseguirà A(). Tuttavia a, b, c deve essere interrompibile, che è quello di controllare la discussione periodicamente corrente interrotto bandiera e gettare InterruptedException, altrimenti non funzionerà

final Thread current = Thread.currentThread(); 
    Thread timer = new Thread() { 
     public void run() { 
      try { 
       Thread.sleep(5000); 
       current.interrupt(); 
      } catch (InterruptedException e) { 
       // timer stopped 
      } 
     }; 
    }; 
    try { 
     A(); // this throws InterruptedException if interrupted by timer 
     timer.interrupt(); // no timeout lets stop the timer 
    } catch (InterruptedException e) { 
     // timeout 
    } 
-1

Vedi anche questo post L'approccio è lasciare che l'applicazione si preoccupi del timeout nella sua logica. Per questo è possibile definire una certa classe timer e metodo di controllo speciale, ad es .:

public class TimeoutApp { 
    MyTimer timer; 
    Thread timerThread; 

    public static void main(String... args) { 
     new TimeoutApp().execute(); 
    } 

    private void execute() { 
     try { 
      startTimer(1000); 
      action1(); 
      checkTimeout(); 
      action2(); 
      checkTimeout(); 
      action3(); 
      stopTimer(); 

     } catch (MyTimeoutException e) { 
      System.out.println("Interrupted on timeout!"); 
      // ...clearing code if needed 
      System.exit(1); 
     } catch (InterruptedException e) { 
      System.out.println("Interrupted by exception!"); 
      // ...clearing code if needed 
      e.printStackTrace(); 
      System.exit(1); 
     } 
    } 

    private void action1() throws InterruptedException { 
     Thread.sleep(600); 
     System.out.println("action 1"); 
    } 

    private void action2() throws InterruptedException { 
     Thread.sleep(500); 
     System.out.println("action 2"); 
    } 

    private void action3() { 
     System.out.println("action 3"); 
    } 

    private void checkTimeout() throws MyTimeoutException { 
     if (timer.isTimeoutReached()) { 
      throw new MyTimeoutException(); 
     } 
    } 

    private void startTimer(long timeout) { 
     timer = new MyTimer(timeout); 
     timerThread = new Thread(timer); 
     timerThread.start(); 
    } 

    private void stopTimer() { 
     timerThread.interrupt(); 
    } 

    private class MyTimer implements Runnable { 
     private long timeout; 
     private boolean timeoutReached = false; 

     public MyTimer(long timeout) { 
      this.timeout = timeout; 
     } 

     public void run() { 
      long time = System.currentTimeMillis(); 
      while (!timeoutReached && !Thread.interrupted()) { 
       if ((System.currentTimeMillis() - time) > timeout) { 
        timeoutReached = true; 
       } 
      } 
     } 

     public boolean isTimeoutReached() { 
      return timeoutReached; 
     } 
    } 

    private class MyTimeoutException extends Exception { 
    } 
} 
+0

molto inefficiente. cosa succede se action2() è un'azione a lungo termine e il timeout si verifica tra l'esecuzione? –

0

Non si può fare una chiamata sincrona con un timeout, ma è possibile emulare utilizzando un secondo thread. Questo è un esempio per farlo:

package com.ardevco.example; 

import java.util.Random; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 
import java.util.concurrent.TimeUnit; 
import java.util.concurrent.TimeoutException; 


class ExceptionThrower { 
    public static <R> R throwUnchecked(Throwable t) { 
     return ExceptionThrower.<RuntimeException, R> trhow0(t); 
    } 

    @SuppressWarnings("unchecked") 
    private static <E extends Throwable, R> R trhow0(Throwable t) throws E { 
     throw (E) t; 
    } 
} 

class TestApplicationException1 extends Exception { 
    private static final long serialVersionUID = 1L; 

    public TestApplicationException1(String string) { 
     super(string); 
    } 
}; 

class TestApplicationException2 extends Exception { 
    private static final long serialVersionUID = 1L; 

    public TestApplicationException2(String string) { 
     super(string); 
    } 
}; 

class TestApplicationTimeoutException extends Exception { 
    private static final long serialVersionUID = 1L; 

    public TestApplicationTimeoutException(String string) { 
     super(string); 
    }; 
} 

public class SynchronousTimeoutTester { 

    public static final long SYNC_METHOD_TIMEOUT_IN_MILLISECONDS = 2000L; 
    private final ExecutorService executorService = Executors.newSingleThreadExecutor(); 

    public static void main(String[] args) { 
     SynchronousTimeoutTester tester = new SynchronousTimeoutTester(); 
     /* call the method asynchronously 10 times */ 
     for (int i = 0; i < 10; i++) { 
     try { 
      System.out.println("Result sync call: " + tester.getAsynchTest()); 
     } 
     catch (TestApplicationException1 e) { 
      System.out.println("catched as TestApplicationException1: " + e); 
     } 
     catch (TestApplicationException2 e) { 
      System.out.println("catched as TestApplicationException2: " + e); 
     } 
     catch (TestApplicationTimeoutException e) { 
      System.out.println("catched as TestApplicationTimeoutException: " + e); 
     } 
     catch (InterruptedException e) { 
      System.out.println("catched as InterruptedException: " + e); 
     } 
     catch (Exception e) { 
      System.out.println("catched as Exception: " + e); 
     } 
     } 

     tester.shutdown(); 
    } 

    private void shutdown() { 
     executorService.shutdown(); 
     try { 
     executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); 
     } 
     catch (InterruptedException e) { 
     System.out.println("Error stopping threadpool:" + e); 
     } 
    } 

    private Integer testAsynch() throws TestApplicationException1, TestApplicationException2, InterruptedException { 
     Random random = new Random(); 
     switch (random.nextInt(10)) { 
     case 0: 
      return 0; 
     case 1: 
      throw new TestApplicationException1("thrown TestApplicationException1"); 
     case 2: 
      throw new TestApplicationException2("thrown TestApplicationException2"); 
     case 3: 
      Thread.sleep(10000L); 
      return -1; 
     case 4: 
      throw new RuntimeException("thrown Exception"); 
     default: 
      return random.nextInt(10); 
     } 
    } 

    private Integer getAsynchTest() throws TestApplicationException1, TestApplicationException2, Exception { 
     Integer dummy = null; 

     Future<Integer> testAsynchF = executorService.submit(
                  new Callable<Integer>() { 
                   public Integer call() throws Exception { 
                   return testAsynch(); 
                   } 
                  }); 

     try { 
     dummy = testAsynchF.get(SynchronousTimeoutTester.SYNC_METHOD_TIMEOUT_IN_MILLISECONDS, TimeUnit.MILLISECONDS); 
     } 
     catch (ExecutionException e1) { 
     System.out.println("in getAsynchTest: ExecutionException: " + e1); 
     ExceptionThrower.throwUnchecked(e1.getCause()); 
     } 
     catch (TimeoutException e1) { 
     System.out.println("in getAsynchTest: TimeoutException: " + e1); 
     throw new TestApplicationTimeoutException("TimeoutException" + e1); 
     } 
     catch (InterruptedException e1) { 
     System.out.println("in getAsynchTest: InterruptedException: " + e1); 
     throw new Exception(e1); 
     } 

     return dummy; 
    } 

}