[Programación concurrente de Java] Mecanismo de interrupción de subprocesos (complementado con casos comunes)


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 interruptenví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();
    }
}

maint.interrupt()Los subprocesos interrumpen tlos subprocesos llamando a . Sin embargo, debe tenerse en cuenta que interrupt()el método solo tenvía una "solicitud de interrupción" al subproceso y solo establece tel atributo de identificación del subproceso en interrupción. tSi el subproceso responde o maneja la solicitud de interrupción depende del tcódigo específico del subproceso.

Aquí, tel 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étodo wait(long)de wait(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 InterruptedExceptionse generará una excepción. La forma general de manejar esta situación es detectar InterruptedExceptionla 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.

Supongo que te gusta

Origin blog.csdn.net/yuchangyuan5237/article/details/132252081
Recomendado
Clasificación