Directorio de artículos
Este artículo presenta el mecanismo de interrupción, los casos comunes y los escenarios de uso de interrupciones desde lo más superficial a lo más profundo.
1. ¿Por qué es necesario?
Por alguna razón, es necesario cancelar el hilo que se estaba ejecutando originalmente. Demos unas cuantas castañas:
- Supongamos que al jugar tiros penales de fútbol, el equipo A anotó 4 goles en las primeras 4 rondas y el equipo B solo anotó 2 goles en las primeras 4 rondas. En este momento, el resultado ya está decidido y no es necesario lanzar el penal. patada en la ronda 5. En este momento, es necesario detener el hilo del equipo A y el hilo del equipo B (un total de 5 penalizaciones).
- Supongamos que el usuario descarga un archivo de 50M de Internet. Si la red es muy lenta y el usuario está impaciente por esperar, hace clic para cancelar la operación de descarga. En este momento, es necesario detener el hilo de descarga.
2. Cómo entender
La interrupción puede entenderse como un atributo de bit de identificación interno de un hilo , que indica si un hilo en ejecución ha sido interrumpido por otros hilos.
La interrupción significa que otros subprocesos interrupt
envían una señal al subproceso a través de un método. Después de recibir la señal, el subproceso puede elegir si responder o no a la señal. La interrupción es solo un mecanismo de negociación . Responder a una señal generalmente finaliza run()
la ejecución de su propio método y sale del hilo, si no responde la ejecución continuará sin ningún impacto.
El hilo responde detectando si está interrumpido. Hay dos métodos de detección, a saber public static boolean interrupted()
y public boolean isInterrupted()
. El método estático borrará el atributo del bit del indicador de interrupción y el método no estático no borrará el atributo del bit del indicador de interrupción.
3. Cómo utilizar
3.1. API relacionadas con la interrupción
Métodos sobre interrupción en el código fuente de Thread:
public class Thread implements Runnable {
// 仅仅只是设置中断状态
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
// 会清除中断状态
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// 不会清除中断状态
public boolean isInterrupted() {
return isInterrupted(false);
}
private native void interrupt0();
private native boolean isInterrupted(boolean ClearInterrupted);
}
Trazado en una tabla:
API | efecto |
---|---|
interrupción pública nula() | Interrumpir este hilo |
público estático booleano interrumpido() | Pruebe si el hilo actual está interrumpido y se borrará el estado de interrupción del hilo |
booleano público está interrumpido () | Pruebe si este hilo está interrumpido. El estado de interrupción no se verá afectado por este método. |
Lo que necesita especial atención es que el método de interrupción estática borrará el estado de interrupción del hilo.
3.2 Interrupción de subprocesos en estado normal
Interrumpir un hilo es muy simple. Solo necesita llamar a un método en el hilo de destino en otros hilos interrupt()
. El hilo de destino necesita verificar repetidamente si su estado está interrumpido. Si es así, finalizará la operación inmediatamente.
public class Main extends Thread {
@Override
public void run() {
int i = 0;
while (!Thread.currentThread().isInterrupted()) {
i++;
System.out.println();
}
System.out.println("done");
}
public static void main(String[] args) throws InterruptedException {
Thread t = new Main();
t.start();
t.sleep(1000);
t.interrupt();
}
}
main
t.interrupt()
Los subprocesos interrumpen t
los subprocesos llamando a . Sin embargo, debe tenerse en cuenta que interrupt()
el método solo t
envía una "solicitud de interrupción" al subproceso y solo establece t
el atributo de identificación del subproceso en interrupción. t
Si el subproceso responde o maneja la solicitud de interrupción depende del t
código específico del subproceso.
Aquí, t
el hilo se repite en el ciclo while para detectar solicitudes de interrupción ( isInterrupted()
). Si se detecta que el hilo está interrumpido, saltará del ciclo while para finalizar el hilo run()
.
3.3 Interrupción de subprocesos en estados especiales
En realidad, hay varios estados especiales en el sitio web oficial de Thread, aquí solo enumeramos los más comunes.
si hilo
- está bloqueado en el
wait()
métodowait(long)
dewait(long, int)
la clase Objeto - O bloqueado en la clase Thread
join()
,join(long)
,join(long, int)
,sleep(long)
,sleep(long, int)
Si se llama al método de interrupción del hilo en este momento interrupt()
, el estado de interrupción del hilo se borrará y InterruptedException
se generará una excepción. La forma general de manejar esta situación es detectar InterruptedException
la excepción y luego restablecer el estado de la interrupción, es decir, llamarThread.currentThread().interrupt();
Veamos el código de muestra:
public class Main extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
// 模拟任务代码
Thread.sleep(2000);
} catch (InterruptedException e) {
// ... 清理操作
System.out.println(isInterrupted());//false
// 重设中断标志位为true
Thread.currentThread().interrupt();
}
}
System.out.println(isInterrupted());//true
}
public static void main(String[] args) {
Main t = new Main();
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
t.interrupt();
}
}
4. Cómo detener un hilo de forma segura
Como se mencionó anteriormente, el estado de interrupción es solo un bit de identificación del subproceso, y la operación de interrupción es una forma simple de interacción entre subprocesos, y este método de interacción es el más adecuado para cancelar o detener tareas. Además de las interrupciones, también puedes usar una variable booleana para controlar si necesitas detener la tarea y finalizar el hilo. Generalmente, los subprocesos se pueden detener de forma segura de las siguientes maneras:
- Modo de interrupción
- Método de variable booleana modificada volátil
Los ejemplos de código son los siguientes:
public class Shutdown {
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread t = new Thread(one, "t");
t.start();
// 睡眠1秒,让Runner线程充分运行,随后能够感知中断而结束
TimeUnit.SECONDS.sleep(1);
t.interrupt();
Runner two = new Runner();
t = new Thread(two, "t");
t.start();
// 睡眠1秒,让Runner线程充分运行,随后能够感知on为false而结束
TimeUnit.SECONDS.sleep(1);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
}
5. Referencias
"El arte de la programación concurrente en Java", puede contactar al autor para descargar la versión electrónica reproducible del libro en pdf sin ningún truco.
Código fuente y comentarios de la clase Thread oficial.