Java Language Gestione di InterruptedException


Esempio

InterruptedException è una bestia che confonde - si presenta in metodi apparentemente innocui come Thread.sleep() , ma Thread.sleep() modo errato porta a un codice difficile da gestire che si comporta male negli ambienti concorrenti.

Nella sua forma più semplice, se viene rilevata un'interruzione di InterruptedException , significa che qualcuno, da qualche parte, ha chiamato Thread.interrupt() sul thread in cui è attualmente in esecuzione il codice. Potresti essere incline a dire "È il mio codice! Non lo interromperò mai! " e quindi fare qualcosa del genere:

// Bad. Don't do this.
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // disregard
}

Ma questo è esattamente il modo sbagliato di gestire un evento "impossibile" che si verifica. Se sai che la tua applicazione non incontrerà mai un'interruzione di InterruptedException , dovresti trattare tale evento come una grave violazione delle ipotesi del tuo programma e uscire il prima possibile.

Il modo corretto di gestire un interrupt "impossibile" è come questo:

// When nothing will interrupt your code
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  throw new AssertionError(e);
}

Questo fa due cose; ripristina innanzitutto lo stato di interruzione del thread (come se l' InterruptedException non fosse stato gettato in primo luogo), quindi lancia un AssertionError indica che gli invarianti di base della tua applicazione sono stati violati. Se si è certi che non si interromperà mai il thread, questo codice viene eseguito in questo modo, poiché il blocco catch non dovrebbe mai essere raggiunto.

L'uso della classe Uninterruptibles di Guava aiuta a semplificare questo schema; chiamando Uninterruptibles.sleepUninterruptibly() ignora lo stato interrotto di un thread fino a quando la durata del sonno è scaduta (a quel punto viene ripristinata per le chiamate successive da ispezionare e lanciare la propria InterruptedException ). Se sai che non interromperesti mai questo codice, eviterai in modo sicuro di dover avvolgere le tue chiamate a riposo in un blocco try-catch.

Più spesso, tuttavia, non è possibile garantire che il thread non verrà mai interrotto. In particolare se stai scrivendo un codice che verrà eseguito da un Executor o da qualche altra gestione dei thread, è fondamentale che il tuo codice risponda prontamente agli interrupt, altrimenti l'applicazione si fermerà o si bloccherà.

In questi casi, la cosa migliore da fare è in genere consentire a InterruptedException di propagare lo stack delle chiamate, aggiungendo una throws InterruptedException di throws InterruptedException a ciascun metodo a turno. Questo può sembrare imbarazzante, ma in realtà è una proprietà desiderabile - le firme del tuo metodo ora indicano ai chiamanti che risponderanno prontamente alle interruzioni.

// Let the caller determine how to handle the interrupt if you're unsure
public void myLongRunningMethod() throws InterruptedException {
  ...
}

In casi limitati (ad es. Durante l'override di un metodo che non throw eccezioni controllate) è possibile ripristinare lo stato interrotto senza generare un'eccezione, aspettandosi che qualsiasi codice venga eseguito accanto a gestire l'interrupt. Questo ritarda la gestione dell'interruzione ma non la sopprime completamente.

// Suppresses the exception but resets the interrupted state letting later code
// detect the interrupt and handle it properly.
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  return ...; // your expectations are still broken at this point - try not to do more work.
}