Executors Java: come posso interrompere le attività inviate?
-
07-07-2019 - |
Domanda
Ho inviato un'attività usando gli esecutori e ho bisogno che si fermi dopo un po 'di tempo (ad es. 5 minuti). Ho provato a fare così:
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 ricevo sempre un errore: ho un vettore condiviso che deve essere modificato dalle attività e quindi letto da un thread, e anche se interrompo tutte le attività, 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?
Soluzione
Solo perché chiami cancel ()
su Future
non significa che l'attività si interromperà automaticamente. Devi fare un po 'di lavoro all'interno dell'attività per assicurarti che si fermi:
- Usa
cancel (true)
in modo che un interrupt venga inviato all'attività. - Gestisci
InterruptedException
. Se una funzione nella tua attività genera unInterruptedException
, assicurati di uscire con garbo il più presto possibile dopo aver rilevato l'eccezione. - Controlla 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 articoli hanno menzionato: ConcurrentModificationException
può essere lanciato anche se si utilizza la classe Vector
thread-safe, perché gli iteratori ottenuti da Vector non sono thread-safe e quindi devono essere sincronizzati. Il for-loop avanzato utilizza iteratori, quindi fai 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);
}
}
Altri suggerimenti
Il ConcurrentModificationException
proviene dalla tua chiamata a task.clear ()
mentre i tuoi Exceutor stanno ripetendo le tue attività
Vector
. Quello che puoi provare a fare è chiamare shutdownNow ()
sul tuo ExecutorService
Il caso più comune di ConcurrentModificationException
è quando il vettore
viene modificato contemporaneamente alla sua iterazione. Spesso questo sarà fatto in un singolo thread. È necessario tenere premuto un blocco sul Vector
per l'intera iterazione (e fare attenzione a non deadlock).
fut.get () è una chiamata bloccante, anche dopo il timeout, si bloccherà fino al completamento dell'attività. Se vuoi fermarti il ??più vicino possibile al segno dei 5 minuti, devi controllare il flag di interruzione, ti consiglio solo di farlo usando il metodo Thread.isInterrupted () che conserva lo stato di interruzione. Se vuoi fermarti immediatamente e non hai bisogno di pulire nessuno stato, lancia un'eccezione che verrà catturata dal futuro e ti verrà indicata come ExecutionException.
fut.cancel (true) non fa nulla in quanto il metodo invokeAll () lo ha già fatto per te.
A meno che tu non usi le " attività " Raccolta da qualche altra parte, probabilmente non è necessario chiamare clear () su di esso. Questo non sarà la fonte del tuo problema poiché il metodo invokeAll () è fatto con la Lista quando chiami clear (). Ma, se devi iniziare a formare un elenco di nuove attività da eseguire, ti suggerisco di formare un nuovo Elenco di attività, non utilizzare un vecchio Elenco di nuove attività.
Sfortunatamente, non ho una risposta per il tuo problema. Non vedo abbastanza informazioni qui per diagnosticare. Nulla nello snippet di codice fornito indica un uso improprio (solo non necessario) delle classi / metodi della libreria. Forse se hai incluso una traccia dello stack completo, anziché l'errore di una riga.
Inserisci fut.cancel (true);
nel blocco finally