2009-09-13 3 views
9

Ho inviato un'attività utilizzando gli executors e ho bisogno che si fermi dopo un po 'di tempo (ad esempio 5 minuti). Ho provato a fare in questo modo:Esecutori Java: come posso interrompere le attività inoltrate?

for (Future<?> fut : e.invokeAll(tasks, 300, TimeUnit.SECONDS)) { 
     try { 
      fut.get(); 
     } catch (CancellationException ex) { 
      fut.cancel(true); 
      tasks.clear(); 
     } catch(ExecutionException ex){ 
      ex.printStackTrace(); //FIXME: gestita con printstack  
     } 
    } 

ma ottengo sempre un errore: ho un vettore condivisa che deve essere modificato dai compiti e poi leggere a un filo, e anche se mi fermo tutta l'operazione, se si verifica il timeout, ottengo:

Exception in thread "Thread-1" java.util.ConcurrentModificationException 

C'è qualcosa che non va? Come posso interrompere le attività inviate che funzionano ancora dopo 5 minuti?

+0

@Raffaele Di Fazio: Ho formattato il codice - e ha aggiunto una stretta doppietta, si prega di verificarne la correttezza. – akf

+0

Grazie, mi dispiace per la formattazione sbagliata. – Raffo

risposta

20

Solo perché chiami cancel() su Future non significa che l'attività si fermerà automaticamente. È necessario fare un certo lavoro entro il compito di fare in modo che si fermerà:

  • Usa cancel(true) in modo che un interrupt viene inviato al compito.
  • Maniglia InterruptedException. Se una funzione nell'attività genera uno InterruptedException, assicurati di uscire con garbo il prima possibile al rilevamento dell'eccezione.
  • Controllare periodicamente Thread.currentThread().isInterrupted() se l'attività esegue il calcolo continuo.

Ad esempio:

class LongTask implements Callable<Double> { 
    public Double call() { 

     // Sleep for a while; handle InterruptedException appropriately 
     try { 
      Thread.sleep(10000); 
     } catch (InterruptedException ex) { 
      System.out.println("Exiting gracefully!"); 
      return null; 
     } 


     // Compute for a while; check Thread.isInterrupted() periodically 
     double sum = 0.0; 
     for (long i = 0; i < 10000000; i++) { 
      sum += 10.0 
      if (Thread.currentThread().isInterrupted()) { 
       System.out.println("Exiting gracefully"); 
       return null; 
      } 
     } 

     return sum; 
    } 
} 

Inoltre, come altri posti hanno detto: ConcurrentModificationException possono essere gettati, anche se si utilizza il Vector classe thread-safe, perché iteratori si ottiene da Vector non sono thread-safe, e quindi deve essere sincronizzato. L'avanzato per-loop utilizza iteratori, quindi attenzione:

final Vector<Double> vector = new Vector<Double>(); 
vector.add(1.0); 
vector.add(2.0); 

// Not thread safe! If another thread modifies "vector" during the loop, then 
// a ConcurrentModificationException will be thrown. 
for (Double num : vector) { 
    System.out.println(num); 
} 

// You can try this as a quick fix, but it might not be what you want: 
synchronized (vector) { // "vector" must be final 
    for (Double num : vector) { 
     System.out.println(num); 
    } 
} 
+0

Eccellente - in qualche modo non mi sono mai imbattuto in Thread. interrupted() - Posso usarlo domani! –

+7

Innanzitutto, chiamare future.cancel (true) non fa assolutamente nulla. Il contratto di invokeAll afferma che annullerà le attività prima di tornare, e l'implementazione utilizza un blocco finale per assicurarlo. In secondo luogo, non chiamare mai Thread.interrupted(), così facendo cancella lo stato interrotto del thread. La maggior parte delle implementazioni vorrebbe usare Thread.isInterrupted(). Cancellare la bandiera dovrebbe essere scrutinato. In terzo luogo, non deve gestire InterruptedException a meno che non stia utilizzando metodi di blocco come l'acquisizione del blocco, e quindi il compilatore si accerti che lo sia. Il FutureTask catturerà eccezioni. –

+1

@Tim Bender: hai ragione: future.cancel (true) non fa nulla, testato da solo. Ma non ho capito cosa pensi che dovrei fare .. – Raffo

0

Il caso più comune per ConcurrentModificationException è quando lo vector viene modificato nello stesso momento in cui viene iterato. Spesso questo sarà fatto in un singolo thread. È necessario tenere premuto il tasto Vector per l'intera iterazione (e fare attenzione a non bloccarlo).

+0

Sì, so perché viene lanciata l'eccezione, ma non dovrebbe. L'iterazione segue la parte del codice che ho postato e quindi, se il codice funziona bene, non dovrei ottenere un'eccezione ... – Raffo

1

Il ConcurrentModificationException viene dalla tua chiamata allo tasks.clear() mentre il tuo Exceutor sta iterando sul tuo tasksVector. Quello che puoi provare è chiamare shutdownNow() sul tuo ExecutorService

+0

Questo non sembra funzionare ... – Raffo

0

fut.get() è una chiamata di blocco, anche dopo il timeout, si bloccare fino a quando il compito è fatto. Se vuoi fermarti il ​​più vicino possibile al segno dei 5 minuti, devi controllare il flag di interrupt, ti consiglio semplicemente di farlo usando il metodo Thread.isInterrupted() che preserva lo stato dell'interrupt. Se vuoi fermarti immediatamente e non è necessario pulire alcun stato, getta un'eccezione che verrà catturata dal futuro e indicata come ExecutionException.

fut.cancel (true) non esegue nulla poiché il metodo invokeAll() lo ha già fatto.

A meno che non si usi la raccolta "tasks" da qualche altra parte, probabilmente non è necessario chiamare clear() su di esso. Questa non sarà la fonte del tuo problema dal momento che il metodo invokeAll() viene eseguito con l'Elenco quando chiami clear(). Ma, se hai bisogno di iniziare a formare un elenco di nuovi compiti da eseguire, ti suggerisco di creare un nuovo elenco di attività, non di usare un vecchio elenco di nuovi compiti.

Purtroppo, non ho una risposta per il tuo problema. Qui non vedo abbastanza informazioni per diagnosticarlo. Nulla nel frammento di codice che hai fornito indica un uso improprio (solo non necessario) di classi/metodi di libreria. Forse se hai incluso una traccia stack completa, invece dell'errore di una riga.

+0

Ho usato la raccolta da qualche altra parte, ed è in un ciclo while, quindi deve essere cancellata per essere vuota quando il ciclo si ripete. Ovviamente posso fare il clear() dopo il codice mostrato nel post, e questo dovrebbe essere ok. La parte importante della mia domanda non è l'eccezione: quello che devo sapere è come fermare il futuro dopo 5 minuti e, ovviamente, proverò a lanciare un'eccezione come suggerito. Posso persino cambiare il modo in cui invio i miei compiti. Ho imparato questo modo qui: http://stackoverflow.com/questions/1322147/help-with-java-executors-wait-for-task-termination – Raffo

-1

Mettere il fut.cancel(true); nel blocco finally

+0

Intendi in un blocco 'finally' da aggiungere, giusto? –