Gestione InterruptedException in Java


Qual è la differenza tra i seguenti modi di gestire InterruptedException? Qual è il modo migliore per farlo?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

O

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

EDIT: mi piacerebbe anche sapere in quali scenari sono usati questi due.

Author: Nathan Hughes, 2010-10-20

7 answers

Probabilmente sei venuto a fare questa domanda perché hai chiamato un metodo che lancia InterruptedException.

Prima di tutto, dovresti vedere throws InterruptedException per quello che è: una parte della firma del metodo e un possibile risultato della chiamata del metodo che stai chiamando. Quindi inizia abbracciando il fatto che un InterruptedException è un risultato perfettamente valido della chiamata al metodo.

Ora, se il metodo che stai chiamando genera tale eccezione, cosa dovrebbe fare il tuo metodo? Si può capire la risposta da pensando a quanto segue:

Ha senso per il metodo che stai implementando per lanciare un InterruptedException? In altre parole, un InterruptedException è un risultato ragionevole quando si chiama il proprio metodo?

  • Se , allora throws InterruptedException dovrebbe essere parte di la tua firma del metodo e dovresti lasciare che l'eccezione si propaghi (cioè non prenderla affatto).

    Esempio: il metodo attende un valore dal rete per completare il calcolo e restituire un risultato. Se la chiamata di rete di blocco genera un InterruptedException, il metodo non può terminare il calcolo in modo normale. Hai lasciato che il InterruptedException si propagasse.

    int computeSum(Server server) throws InterruptedException {
        // Any InterruptedException thrown below is propagated
        int a = server.getValueA();
        int b = server.getValueB();
        return a + b;
    }
    
  • Se no , allora non dovresti dichiarare il tuo metodo con throws InterruptedException e dovresti (devi!) cattura l'eccezione. Ora due cose sono importanti da tenere a mente in questa situazione:

    1. Qualcuno ha interrotto il tuo thread. Che qualcuno è probabilmente desideroso di annullare l'operazione, terminare il programma con grazia, o qualsiasi altra cosa. Dovresti essere educato con quel qualcuno e tornare dal tuo metodo senza ulteriori indugi.

    2. Anche se il tuo metodo può riuscire a produrre un valore di ritorno ragionevole in caso di InterruptedException il fatto che il thread sia stato interrotto potrebbe ancora essere importante. In particolare, il codice che chiama il metodo potrebbe essere interessato a sapere se si è verificata un'interruzione durante l'esecuzione del metodo. Si dovrebbe quindi registrare il fatto che un'interruzione ha avuto luogo impostando il flag interrotto: Thread.currentThread().interrupt()

    Esempio: L'utente ha chiesto di stampare una somma di due valori. La stampa di " Failed to compute sum" è accettabile se la somma non può essere calcolata (e molto meglio che lasciare che il programma si blocchi con una traccia di stack a causa di un InterruptedException). In altre parole, non ha senso dichiarare questo metodo con throws InterruptedException.

    void printSum(Server server) {
         try {
             int sum = computeSum(server);
             System.out.println("Sum: " + sum);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();  // set interrupt flag
             System.out.println("Failed to compute sum");
         }
    }
    

Ormai dovrebbe essere chiaro che solo fare throw new RuntimeException(e) è una cattiva idea. Non è molto gentile con chi chiama. Potresti inventare una nuova eccezione di runtime ma la causa principale (qualcuno vuole che il thread interrompa l'esecuzione) potrebbe perdersi.

Altri esempi:

Attuazione Runnable: Come avrai scoperto, la firma di Runnable.run non consente di ripensare InterruptedExceptions. Bene, tu ti sei iscritto all'implementazione di Runnable, il che significa che tu hai firmato per affrontare possibile InterruptedExceptions. Scegliere un'interfaccia diversa, ad esempio Callable, oppure segui il secondo approccio sopra.

 

Chiamata Thread.sleep: Stai tentando di leggere un file e le specifiche dicono che dovresti provare 10 volte con 1 secondo in mezzo. Si chiama Thread.sleep(1000). Quindi, devi occuparti di InterruptedException. Per un metodo come {[27] } ha perfettamente senso dire, " Se vengo interrotto, non riesco a completare la mia azione di cercare di leggere il file " . In altre parole, ha perfettamente senso che il metodo lanci InterruptedExceptions.

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

Questo post è stato riscritto come articolo qui.

 298
Author: aioobe, 2016-11-28 12:23:39

Guarda caso stavo leggendo di questo questa mattina sul mio modo di lavorare in Java Concurrency In Practice di Brian Goetz. Fondamentalmente dice che dovresti fare una delle due cose

  1. Propagare il InterruptedException - Dichiara il tuo metodo per lanciare il InterruptedException controllato in modo che il tuo chiamante debba gestirlo.

  2. Ripristina l'Interrupt - A volte non puoi lanciare InterruptedException. In questi casi si dovrebbe prendere il InterruptedException e ripristinare l'interrupt stato chiamando il metodo interrupt() sul currentThread in modo che il codice più in alto nello stack di chiamate possa vedere che è stato emesso un interrupt.

 73
Author: mR_fr0g, 2014-02-24 13:04:54

Cosa stai cercando di fare?

Il InterruptedException viene lanciato quando un thread è in attesa o in sospensione e un altro thread lo interrompe usando il metodo interrupt nella classe Thread. Quindi, se si cattura questa eccezione, significa che il thread è stato interrotto. Di solito non ha senso chiamare di nuovo Thread.currentThread().interrupt();, a meno che non si voglia controllare lo stato "interrotto" del thread da qualche altra parte.

Per quanto riguarda l'altra opzione di lanciare un RuntimeException, non sembra una cosa molto saggia da fare (chi lo farà capito? come sarà gestito?) ma è difficile dire di più senza ulteriori informazioni.

 16
Author: Grodriguez, 2010-10-20 09:30:24

Per me la cosa fondamentale di questo è: un'InterruptedException non è qualcosa che va storto, è il thread che fa quello che gli hai detto di fare. Pertanto, ripensarlo avvolto in una RuntimeException non ha senso.

In molti casi ha senso ripensare un'eccezione racchiusa in una RuntimeException quando dici, non so cosa sia andato storto qui e non posso fare nulla per risolverlo, voglio solo che esca dal flusso di elaborazione corrente e colpisca qualsiasi eccezione a livello di applicazione gestore che ho in modo che possa registrarlo. Questo non è il caso di un'InterruptedException, è solo il thread che risponde ad avere interrupt() chiamato su di esso, sta lanciando l'InterruptedException per aiutare ad annullare l'elaborazione del thread in modo tempestivo.

Quindi propagare InterruptedException o mangiarlo in modo intelligente (ovvero in un punto in cui avrà realizzato ciò che doveva fare) e reimpostare il flag di interrupt. Si noti che il flag di interrupt viene cancellato quando il InterruptedException viene generata; l'ipotesi che gli sviluppatori della libreria Jdk fanno è che catturare l'eccezione equivale a gestirla, quindi per impostazione predefinita il flag viene cancellato.

Quindi sicuramente il primo modo è migliore, il secondo esempio pubblicato nella domanda non è utile a meno che non ci si aspetti che il thread venga effettivamente interrotto e l'interruzione equivale a un errore.

Ecco una risposta che ho scritto che descrive come funzionano gli interrupt, con un esempio . Si può vedere nel codice di esempio in cui utilizza InterruptedException per salvare un ciclo while nel metodo Runnable.

 11
Author: Nathan Hughes, 2017-05-23 11:54:40

La scelta predefinita corretta è aggiungi InterruptedException alla tua lista di lanci. Un Interrupt indica che un altro thread desidera che il thread finisca. Il motivo di questa richiesta non è reso evidente ed è del tutto contestuale, quindi se non si dispone di alcuna conoscenza aggiuntiva si dovrebbe presumere che sia solo un arresto amichevole, e tutto ciò che evita quell'arresto è una risposta non amichevole.

Java non lancerà casualmente InterruptedException, tutti i consigli non influenzeranno il tuo applicazione ma ho incontrato un caso in cui lo sviluppatore ha seguito la strategia "swallow" è diventato molto scomodo. Un team aveva sviluppato una vasta serie di test e filo utilizzato.Dormi molto. Ora abbiamo iniziato a eseguire i test nel nostro server CI e, a volte, a causa di difetti nel codice, rimanevamo bloccati in attese permanenti. Per peggiorare la situazione, quando si tenta di annullare il lavoro CI non si è mai chiuso perché il Thread.L'interruzione che aveva lo scopo di interrompere il test non ha interrotto il lavoro. Abbiamo avuto per accedere alla casella e uccidere manualmente i processi.

Per farla breve, se si lancia semplicemente InterruptedException, si corrisponde all'intento predefinito che il thread dovrebbe terminare. Se non riesci ad aggiungere InterruptedException alla tua lista di lancio, lo avvolgerei in una RuntimeException.

C'è un argomento molto razionale da fare che InterruptedException dovrebbe essere una RuntimeException stessa, poiché ciò incoraggerebbe una migliore gestione "predefinita". Non è una RuntimeException solo perché i progettisti si sono attenuti a una regola categorica che una RuntimeException dovrebbe rappresentare un errore nel codice. Poiché InterruptedException non deriva direttamente da un errore nel codice, non lo è. Ma la realtà è che spesso un'InterruptedException sorge perché c'è un errore nel tuo codice, (cioè loop infinito, dead-lock), e l'Interrupt è il metodo di qualche altro thread per gestire quell'errore.

Se sai che c'è una pulizia razionale da fare, allora fallo. Se conosci una causa più profonda per l'Interrupt, puoi assumere una gestione più completa.

Quindi in sintesi le tue scelte per la gestione dovrebbero seguire questo elenco:

  1. Per impostazione predefinita, aggiungi ai tiri.
  2. Se non è consentito aggiungere ai lanci, lanciare RuntimeException (e). (scelta migliore tra più opzioni errate)
  3. Solo quando si conosce una causa esplicita dell'Interrupt, gestire come desiderato. Se la gestione è locale al metodo, reimpostare interrotto da una chiamata infilare.currentThread ().interrompere().
 6
Author: nedruod, 2013-09-20 21:32:50

Volevo solo aggiungere un'ultima opzione a ciò che la maggior parte delle persone e degli articoli menzionano. Come ha affermato mR_fr0g, è importante gestire correttamente l'interrupt tramite:

  • Propagazione dell'InterruptException

  • Ripristina lo stato di interrupt sul thread

O in aggiunta:

  • Gestione personalizzata dell'Interrupt

Non c'è niente di sbagliato nel gestire l'interrupt in modo personalizzato a seconda delle circostanze. Come interruzione è una richiesta di terminazione, al contrario di un comando forzato, è perfettamente valido completare il lavoro aggiuntivo per consentire all'applicazione di gestire la richiesta con garbo. Ad esempio, se un thread sta dormendo, in attesa di IO o di una risposta hardware, quando riceve l'Interrupt, allora è perfettamente valido chiudere con grazia qualsiasi connessione prima di terminare il thread.

Consiglio vivamente di comprendere l'argomento, ma questo articolo è una buona fonte di informazioni: http://www.ibm.com/developerworks/java/library/j-jtp05236 /

 1
Author: TheIT, 2013-12-17 02:38:10

Direi che in alcuni casi è ok non fare nulla. Probabilmente non è qualcosa che dovresti fare di default, ma nel caso in cui non ci sia modo che l'interrupt si verifichi, non sono sicuro di cos'altro fare (probabilmente errore di registrazione, ma ciò non influisce sul flusso del programma).

Un caso sarebbe nel caso in cui tu abbia una coda di attività (blocco). Nel caso in cui si disponga di un thread daemon che gestisce queste attività e non si interrompe il Thread da soli (a mia conoscenza la jvm non interrompe il demone thread sull'arresto jvm), non vedo alcun modo per l'interrupt, e quindi potrebbe essere semplicemente ignorato. (So che un thread daemon può essere ucciso dalla jvm in qualsiasi momento e quindi non è adatto in alcuni casi).

MODIFICA: Un altro caso potrebbe essere blocchi custoditi, almeno in base al tutorial di Oracle su: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

 0
Author: Bjarne Boström, 2015-02-25 14:11:42