English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Analisi del meccanismo di interruzione dei thread in Java

Il meccanismo di interruzione del thread fornisce un metodo per svegliare un thread dalla espera bloccata, cercando di interrompere la sequenza di elaborazione attuale del thread di destinazione per farlo rispondere a un nuovo comando. Java lascia questa libertà ai sviluppatori, e dovremmo sfruttarla bene.
Oggi parliamo del meccanismo di interruzione dei thread in Java.

Il meccanismo di interruzione del thread fornisce un metodo, con due usi comuni:

Sveglia il thread dalla espera bloccata e gestisci il trattamento di 'interruzione controllata' corrispondente.
Cerca di informare il thread di destinazione: interrompi la tua attuale sequenza di elaborazione e rispondi a un nuovo comando.
Per esempio, consideriamo l'uso principale, ecco il seguente codice:

synchronized (lock) {
  try {
    while (!check()) {
      lock.wait(1000);
    }
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

Questo codice utilizza il meccanismo wait/notify fornito da Java, la chiamata lock.wait() blocca il thread e ci sono tre casi che permettono al thread di riprendere l'esecuzione.

1. La scadenza di 1000ms termina e si esegue normalmente la prossima istruzione.

2. Un altro thread esegue il seguente codice per svegliare manualmente

synchronized (lock) {
  lock.notifyAll(); // o lock.notify();
}

Questo eseguirà normalmente la prossima istruzione.

3. Un altro thread richiede di interrompere il thread in attesa

// Ottenere un riferimento al thread in attesa
Thread a;
a.interrupt();

Il thread 'a' interrotto, lancia l'eccezione InterruptedException all'interno di lock.wait().

In sintesi, puoi considerare che object.wait() sta facendo queste cose:

boolean checkTimeout = timeout > 0;
Thread current = Thread.currentThread();
lock.addWaiter(current);
while (!current.isNotified()) {
  if (current.isInterrupted()) {
    current.clearInterrupted();
    throw new InterruptedException();
  }
  if (checkTimeout) {
    if (timeout == 0) break;
    timeout--;
  }
}

Questo non è completamente accurato, perché wait non utilizza questo metodo di 'buscando attivamente' per fare controlli, ma la logica di giudizio sul segno di interruzione è corretta.

Partiamo dall'operazione di 'emettere manualmente l'interruzione' menzionata sopra per esplorare

// sun.nio.ch.Interruptible
public interface Interruptible {
  void interrupt(Thread var1);
}
// java.lang.Thread
private volatile Interruptible blocker;
private final Object blockerLock = new Object();
public void interrupt() {
  if (this != Thread.currentThread())
    checkAccess();
  synchronized (blockerLock) {
    Interruptible b = blocker;
    if (b != null) {
      interrupt0();
      b.interrupt(this);
      return;
    }
  }
  interrupt0();
}
// Just to set the interrupt flag
private native void interrupt0();

Si può notare che thread.interrupt() verifica prima i permessi, quindi chiama effettivamente interrupt0() per impostare il segno di interruzione del thread, e se il thread ha l'Interruptible di nio, lo chiama anche di ritorno.

Attenzione, interrupt0() imposta solo il segno di interruzione del thread.

Quando un thread non è bloccato, non è in un'area non controllata dalla logica del programma Java come object.wait(), thread.join(), Thread.sleep() ecc., cosa accade? La risposta è che non accade nulla, se un thread è stato interrotto può essere rilevato solo controllando attivamente il segno di interruzione.

Come controllare? Thread esporre due interfacce, Thread.interrupted() e thread.isInterrupted().

// java.lang.Thread
public static boolean interrupted() {
  return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
  return isInterrupted(false);
}
private native boolean isInterrupted(boolean clearInterrupted);

Si può vedere che entrambi si affidano all'interno di isInterrupted(boolean), che restituisce se il thread è stato interrotto e, se necessario, annulla il flag di interruzione.

Quando una chiamata di funzione potrebbe causare un blocco, le funzioni della libreria Java nel punto di blocco specificano throws InterruptedException e richiedono di scrivere try catch per gestire l'interruzione.

Quando una thread si blocca, come descritto sopra, Java verifica il flag di interruzione, lo annulla e poi lancia InterruptedException.

// java.lang.Object
public final void wait() throws InterruptedException {
  wait(0);
}
public final native void wait(long timeout) throws InterruptedException;

Se un thread riceve InterruptedException e poi esegue ancora codice che potrebbe causare un blocco, continuerà a bloccarsi come se nulla fosse successo. Poiché Java annulla internamente il flag di interruzione!

Solitamente scriviamo i seguenti tre tipi di codice per gestire InterruptedException:

Passa InterruptedException al livello superiore per la gestione.

public void foo() throws InterruptedException {
  synchronized (lock) {
    lock.wait();
  }
}

遇到InterruptedException重设中断标志位。

try {
  synchronized (lock) { 
    lock.wait(); 
  } 
} catch (InterruptedException e) { 
  Thread.currentThread().interrupt();
  //break; 
}

先忙完,再重新抛出InterruptedException。

public void bar() throws InterruptedException {
  InterruptedException ie = null;
  boolean done = false;
  while (!done) {
    synchronized (lock) {
      try {
        lock.wait();
      } catch (InterruptedException e) {
        ie = e;
        continue;
      }
    }
    done = true;
  }
  if (ie != null) {
    throw ie;
  }
}

如果一个线程无视中断标志和InterruptedException,它仍然能够跑得很好。但这与我们设计多线程的初衷是相违背的,我们希望线程之间是和谐的有序协作以实现特定功能,因此受控线程应当对中断作出响应。而Java留给开发者这一自由,我们应当予以善用。

以上就是这次给大家介绍的Java线程的中断机制相关知识的全部内容,如果还有任何不明白的可以在下方的留言区域讨论,感谢对呐喊教程的支持。

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#oldtoolbag.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。

Ti potrebbe interessare