2009-07-22 5 views
72

Esiste un modo standard per chiamare un metodo di blocco con un timeout in Java? Voglio essere in grado di fare:Come chiamo un metodo di blocco con un timeout in Java?

// call something.blockingMethod(); 
// if it hasn't come back within 2 seconds, forget it 

se questo ha un senso.

Grazie.

+1

Come riferimento, check out Java Concurrency in Practice di Brian Goetz pp 126-134, in particolare la sezione 6.3.7 "Immissione limiti di tempo sulle mansioni" –

risposta

123

si potrebbe usare un esecutore:

ExecutorService executor = Executors.newCachedThreadPool(); 
Callable<Object> task = new Callable<Object>() { 
    public Object call() { 
     return something.blockingMethod(); 
    } 
}; 
Future<Object> future = executor.submit(task); 
try { 
    Object result = future.get(5, TimeUnit.SECONDS); 
} catch (TimeoutException ex) { 
    // handle the timeout 
} catch (InterruptedException e) { 
    // handle the interrupts 
} catch (ExecutionException e) { 
    // handle other exceptions 
} finally { 
    future.cancel(true); // may or may not desire this 
} 

Se il future.get non restituisce in 5 secondi, si getta una TimeoutException. Il timeout può essere configurato in secondi, minuti, millisecondi o qualsiasi unità disponibile come costante in TimeUnit.

Vedere JavaDoc per ulteriori dettagli.

+5

Il metodo di blocco continuerà a funzionare anche dopo il timeout, giusto? –

+0

Dipende da future.cancel. A seconda di cosa sta facendo il metodo di blocco al momento, può o non può terminare. – skaffman

+0

testato e lavorato qui. – Jus12

9

È possibile effettuare il wrap della chiamata in uno FutureTask e utilizzare la versione di timeout di get().

Vedi http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html

+0

FutureTask isn'. È esso stesso asincrono, vero? Di per sé fa semplicemente le cose in modo sincrono, è necessario combinarlo con un Executor per gestire il comportamento asynch. – skaffman

+0

Sì, hai bisogno di un esecutore come quello che hai codificato –

1
Thread thread = new Thread(new Runnable() { 
    public void run() { 
     something.blockingMethod(); 
    } 
}); 
thread.start(); 
thread.join(2000); 
if (thread.isAlive()) { 
    thread.stop(); 
} 

nota, quella fermata è deprecato, migliore alternativa è quella di impostare alcuni flag booleano volatile, all'interno blockingMethod() controllare e di uscita, in questo modo:

import org.junit.*; 
import java.util.*; 
import junit.framework.TestCase; 

public class ThreadTest extends TestCase { 
    static class Something implements Runnable { 
     private volatile boolean stopRequested; 
     private final int steps; 
     private final long waitPerStep; 

     public Something(int steps, long waitPerStep) { 
      this.steps = steps; 
      this.waitPerStep = waitPerStep; 
     } 

     @Override 
     public void run() { 
      blockingMethod(); 
     } 

     public void blockingMethod() { 
      try { 
       for (int i = 0; i < steps && !stopRequested; i++) { 
        doALittleBit(); 
       } 
      } catch (InterruptedException e) { 
       throw new RuntimeException(e); 
      } 
     } 

     public void doALittleBit() throws InterruptedException { 
      Thread.sleep(waitPerStep); 
     } 

     public void setStopRequested(boolean stopRequested) { 
      this.stopRequested = stopRequested; 
     } 
    } 

    @Test 
    public void test() throws InterruptedException { 
     final Something somethingRunnable = new Something(5, 1000); 
     Thread thread = new Thread(somethingRunnable); 
     thread.start(); 
     thread.join(2000); 
     if (thread.isAlive()) { 
      somethingRunnable.setStopRequested(true); 
      thread.join(2000); 
      assertFalse(thread.isAlive()); 
     } else { 
      fail("Exptected to be alive (5 * 1000 > 2000)"); 
     } 
    } 
} 
0

Assumere blockingMethod solo dormire per alcune Millis:

public void blockingMethod(Object input) { 
    try { 
     Thread.sleep(3000); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

La mia soluzione è quella di utilizzare wait() e synchronized come questo:

public void blockingMethod(final Object input, long millis) { 
    final Object lock = new Object(); 
    new Thread(new Runnable() { 

     @Override 
     public void run() { 
      blockingMethod(input); 
      synchronized (lock) { 
       lock.notify(); 
      } 
     } 
    }).start(); 
    synchronized (lock) { 
     try { 
      // Wait for specific millis and release the lock. 
      // If blockingMethod is done during waiting time, it will wake 
      // me up and give me the lock, and I will finish directly. 
      // Otherwise, when the waiting time is over and the 
      // blockingMethod is still 
      // running, I will reacquire the lock and finish. 
      lock.wait(millis); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Così u può sostituire

something.blockingMethod(input)

a

something.blockingMethod(input, 2000)

Speranza che aiuta.

3

Esiste anche una soluzione AspectJ con libreria jcabi-aspects.

@Timeable(limit = 30, unit = TimeUnit.MINUTES) 
public Soup cookSoup() { 
    // Cook soup, but for no more than 30 minutes (throw and exception if it takes any longer 
} 

Non potrebbe essere più succinta, ma si deve dipendere da AspectJ e introdurlo nel ciclo di vita di compilazione, naturalmente.

C'è un articolo che spiega ulteriormente: Limit Java Method Execution Time

1

Prova questo. Soluzione più semplice Garantisce che se il blocco non è stato eseguito entro il limite di tempo. il processo terminerà e genererà un'eccezione.

public class TimeoutBlock { 

private final long timeoutMilliSeconds; 
    private long timeoutInteval=100; 

    public TimeoutBlock(long timeoutMilliSeconds){ 
     this.timeoutMilliSeconds=timeoutMilliSeconds; 
    } 

    public void addBlock(Runnable runnable) throws Throwable{ 
     long collectIntervals=0; 
     Thread timeoutWorker=new Thread(runnable); 
     timeoutWorker.start(); 
     do{ 
      if(collectIntervals>=this.timeoutMilliSeconds){ 
       timeoutWorker.stop(); 
       throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated."); 
      } 
      collectIntervals+=timeoutInteval;   
      Thread.sleep(timeoutInteval); 

     }while(timeoutWorker.isAlive()); 
     System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds."); 
    } 

    /** 
    * @return the timeoutInteval 
    */ 
    public long getTimeoutInteval() { 
     return timeoutInteval; 
    } 

    /** 
    * @param timeoutInteval the timeoutInteval to set 
    */ 
    public void setTimeoutInteval(long timeoutInteval) { 
     this.timeoutInteval = timeoutInteval; 
    } 
} 

esempio:

try { 
     TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds 
     Runnable block=new Runnable() { 

      @Override 
      public void run() { 
       //TO DO write block of code 
      } 
     }; 

     timeoutBlock.addBlock(block);// execute the runnable block 

    } catch (Throwable e) { 
     //catch the exception here . Which is block didn't execute within the time limit 
    }