Aprende un poco de subprocesos múltiples todos los días

subprocesos múltiples

1. Conceptos relacionados

Concurrencia y Paralelismo

Paralelo (parallel): se refiere a múltiples tareas de eventos que ocurren al mismo tiempo (simultáneamente).
Concurrencia : Se refiere a dos o más eventos que ocurren dentro del mismo pequeño período de tiempo .La ejecución simultánea de programas puede hacer un uso completo de los recursos de la CPU en condiciones limitadas.
inserte la descripción de la imagen aquí
CPU de un solo núcleo: solo concurrente
CPU de varios núcleos: paralelo + concurrente

Hilos y Procesos

  • Programa : para completar una determinada tarea y función, elija un conjunto de instrucciones escritas en un lenguaje de programación.

  • Software : 1 o más aplicaciones + materiales relacionados y archivos de recursos constituyen un sistema de software.

  • Un proceso es una descripción del proceso en ejecución (creación-ejecución-muerte) de un programa.El sistema crea un proceso para cada programa en ejecución y asigna recursos del sistema independientes, como espacio de memoria, al proceso.

  • Hilo : Un hilo es una unidad de ejecución en un proceso, que es responsable de completar la tarea de ejecutar el programa actual.Hay al menos un hilo en un proceso. Puede haber múltiples subprocesos en un proceso, y este programa de aplicación también puede denominarse programa de subprocesos múltiples en este momento. Los subprocesos múltiples permiten que los programas se ejecuten simultáneamente y aprovechen al máximo los recursos de la CPU.

    Pregunta de la entrevista : un proceso es la unidad más pequeña de programación del sistema operativo y asignación de recursos, y un subproceso es la unidad más pequeña de programación de la CPU. Diferentes procesos no comparten memoria. El costo del intercambio de datos y la comunicación entre procesos es alto. Diferentes hilos comparten la memoria del mismo proceso. Por supuesto, diferentes subprocesos también tienen su propio espacio de memoria independiente. Para el área de métodos, la memoria del mismo objeto en el montón se puede compartir entre subprocesos, pero las variables locales de la pila siempre son independientes.

Ventajas y escenarios de aplicación de subprocesos múltiples

  • La ventaja principal:
    • Aproveche al máximo los intervalos de tiempo de inactividad de la CPU para completar las solicitudes de los usuarios en el menor tiempo posible. Eso es para que el programa responda más rápido.
  • Escenario de aplicación:
    • multitarea Cuando varios usuarios solicitan el servidor, el programa del servidor puede abrir múltiples subprocesos para procesar la solicitud de cada usuario por separado sin afectarse entre sí.
    • Procesamiento de una sola tarea grande. Para descargar un archivo grande, puede abrir varios subprocesos para descargarlos juntos, lo que reduce el tiempo total de descarga.

programación de subprocesos

Se refiere a cómo se asignan los recursos de la CPU a diferentes subprocesos. Dos métodos comunes de programación de subprocesos:

  • programación de tiempo compartido

    Todos los subprocesos se turnan para usar la CPU y cada subproceso ocupa el tiempo de la CPU de manera uniforme.

  • programación preventiva

    Dar prioridad a los subprocesos con alta prioridad para usar la CPU. Si los subprocesos tienen la misma prioridad, uno será seleccionado aleatoriamente (aleatoriedad de subprocesos). Java utiliza un método de programación preventiva .

2. Creación y puesta en marcha de subprocesos

Heredar la clase Thread

Pasos para crear e iniciar subprocesos múltiples heredando la clase Thread:

  1. Defina la subclase de la clase Subproceso y reescriba el método run() de esta clase. El cuerpo del método del método run() representa la tarea que el subproceso debe completar, por lo que el método run() se denomina cuerpo de ejecución del subproceso. .
  2. Cree una instancia de la subclase Thread, es decir, cree un objeto thread
  3. Llame al método start() del objeto hilo para iniciar el hilo

Notas sobre el análisis de ejecución de subprocesos múltiples :
inserte la descripción de la imagen aquí

  • Llamar manualmente al método de ejecución no es la forma de iniciar un hilo, es solo una llamada de método normal.

  • Después de que el método de inicio inicie el subproceso, la JVM llamará y ejecutará el método de ejecución.

  • No inicie el mismo hilo repetidamente, de lo contrario se lanzará una excepciónIllegalThreadStateException

  • No use la unidad Junit para probar subprocesos múltiples, no es compatible, después de que finalice el subproceso principal, llamará para System.exit()salir de la JVM directamente;

Implementar la interfaz Runnable

  1. Defina la clase de implementación de la interfaz Runnable y reescriba el método run() de la interfaz. El cuerpo del método del método run() también es el cuerpo de ejecución del subproceso del subproceso.
  2. Cree una instancia de la clase de implementación Runnable y use esta instancia como destino de Thread para crear un objeto Thread, que es el objeto de hilo real.
  3. Llame al método start() del objeto hilo para iniciar el hilo.

Comparación de dos formas de crear hilos

  • La clase Thread en sí misma también implementa la interfaz Runnable.El método de ejecución proviene de la interfaz Runnable, y el método de ejecución también es la tarea de subproceso real que se ejecutará.

    public class Thread implements Runnable {
          
          }
    
  • Debido a que las clases de Java son de herencia única, la forma de heredar Thread tiene la limitación de la herencia única, pero es más sencilla de usar.

  • La forma de implementar la interfaz Runnable evita la limitación de la herencia única y puede crear múltiples objetos de subprocesos.Comparta un objeto de clase de implementación Runnable (clase de tarea de subproceso), para facilitar la ejecución de tareas de subprocesos múltiplescompartir datos

Subproceso de creación de objeto de clase interna anónimo

Crear subprocesos por medio de objetos anónimos de clase interna no es una nueva forma de crear subprocesos, pero cuando la tarea del subproceso solo debe ejecutarse una vez, no necesitamos crear clases de subprocesos por separado, podemos usar objetos anónimos:

new Thread("新的线程!"){
    
    
	@Override
	public void run() {
    
    
		for (int i = 0; i < 10; i++) {
    
    
			System.out.println(getName()+":正在执行!"+i);
		}
	}
}.start();

new Thread(new Runnable(){
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+":" + i);
        }
    }
}).start();

3. Clase de hilo

Método de construcción

  • subproceso público (): asigna un nuevo objeto de subproceso.
  • Subproceso público (nombre de cadena): asigna un nuevo objeto de subproceso con el nombre especificado.
  • subproceso público (objetivo ejecutable): asigna un nuevo objeto de subproceso con el objetivo especificado.
  • Subproceso público (objetivo ejecutable, nombre de cadena): asigne un nuevo objeto de subproceso con el objetivo especificado y especifique el nombre.

Los hilos usan el método básico.

  • public void run() : Aquí se define la tarea que realizará este subproceso.

  • public String getName() : Obtiene el nombre del hilo actual.

  • subproceso estático público subproceso actual (): devuelve una referencia al objeto de subproceso que se está ejecutando actualmente.

  • public final boolean isAlive(): Comprueba si el hilo está vivo. Activo si el subproceso se ha iniciado y aún no ha terminado.

  • public final int getPriority() : devuelve la prioridad del hilo

  • public final void setPriority(int newPriority): cambiar la prioridad del hilo

    • Cada subproceso tiene una determinada prioridad, y los subprocesos con mayor prioridad obtendrán más oportunidades de ejecución. De forma predeterminada, cada subproceso tiene la misma prioridad que el subproceso principal que lo creó. La clase Thread proporciona las clases de método setPriority(int newPriority) y getPriority() para establecer y obtener la prioridad del hilo, donde el método setPriority requiere un número entero y el rango está entre [1,10]. establece las tres prioridades de las constantes de clase de la clase Thread:
    • MAX_PRIORITY (10): máxima prioridad
    • MIN_PRIORITY (1): prioridad más baja
    • NORM_PRIORITY (5): prioridad normal, por defecto el hilo principal tiene prioridad normal.

Métodos comunes de control de subprocesos

  • public void start() : hace que este subproceso comience a ejecutarse; la máquina virtual Java llama al método de ejecución de este subproceso.

  • public static void sleep (long millis): suspensión de subprocesos, lo que hace que el subproceso que se está ejecutando se pause (deje de ejecutarse temporalmente) durante el número especificado de milisegundos.

  • public static void yield (): cortesía del subproceso, el rendimiento solo hace que el subproceso actual pierda temporalmente el derecho de ejecución, deje que el programador de subprocesos del sistema reprograme, esperando que otros subprocesos con la misma o mayor prioridad que el subproceso actual puedan tener la oportunidad de ejecutar , pero esto No hay garantía Es muy posible que cuando un subproceso llame al método de rendimiento para pausar, el programador del subproceso lo programará para que se vuelva a ejecutar.

  • void join() : Únase a un hilo, agregue un nuevo hilo al hilo actual, espere a que el hilo unido termine antes de continuar con la ejecución del hilo actual.

    void join(long millis): espera a que este subproceso finalice hasta milimilisegundos. Si se acaba el tiempo de milis, no habrá más espera.

    void join(long millis, int nanos) : espera a que el subproceso finalice hasta milis millis + nanos nanosegundos.

  • public final void stop(): Obliga al subproceso a dejar de ejecutarse. Este método no es seguro, está en desuso y no debe usarse.

    • Llamar al método stop() detendrá inmediatamente todo el trabajo restante en el método run(), incluidos los de la instrucción catch o finalmente, y generará una excepción ThreadDeath (por lo general, esta excepción no requiere una captura explícita), por lo que puede causar algunos problemas. No se pueden completar los trabajos de limpieza, como cerrar archivos, bases de datos, etc.
    • Llamar al método stop() liberará inmediatamente todos los bloqueos retenidos por el subproceso, lo que dará como resultado datos no sincronizados e inconsistencias en los datos.
  • public void interrupt(): Interrumpir el subproceso en realidad marca el subproceso como una interrupción, y en realidad no detiene la ejecución del subproceso.

  • booleano estático público interrumpido (): verifique el estado interrumpido del hilo, llamar a este método borrará el estado interrumpido (bandera).

  • public boolean isInterrupted(): comprobar el estado interrumpido del subproceso, no borrará el estado interrumpido (bandera)

  • public void setDaemon(boolean on): Establece el subproceso como un subproceso daemon. Debe configurarse antes de que comience el subproceso; de lo contrario, IllegalThreadStateExceptionse informará una excepción.

    • El subproceso daemon sirve principalmente a otros subprocesos. Cuando no hay ningún subproceso que no sea demonio ejecutándose en el programa, el subproceso daemon también terminará la ejecución. El recolector de basura JVM también es un subproceso de daemon.
  • public boolean isDaemon(): Comprueba si el subproceso actual es un subproceso daemon.

  • La función de volatile es garantizar que no se omitan algunas instrucciones debido a la optimización del compilador. La variable volátil significa que la variable puede cambiarse inesperadamente. Vuelva a leer cuidadosamente el valor de esta variable cada vez en lugar de usar guardar una copia de seguridad en el registrarse, de modo que el compilador no asuma el valor de esta variable.

ciclo de vida del hilo

Cinco estados de subprocesos del modelo de subprocesamiento tradicional
inserte la descripción de la imagen aquí

Los seis estados de hilo definidos por JDK definen una clase de enumeración dentro de la clase para describir los seis estados del hilo
:java.lang.Thread

    public enum State {
    
    
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }

inserte la descripción de la imagen aquí
Descripción: Al regresar desde WAITINGo hacia el estado, si se encuentra que el subproceso actual no ha obtenido el bloqueo del monitor, se transferirá inmediatamente al estado.TIMED_WAITINGRunnableBLOCKED

seguridad del hilo

Cuando usamos múltiples hilos para acceder al mismo recurso (puede ser la misma variable, el mismo archivo, el mismo registro, etc.), pero si hay operaciones de lectura y escritura en el recurso en múltiples hilos, habrá inconsistencia de datos problemas antes y después, que es un problema de seguridad de subprocesos.

Los problemas de seguridad del hilo conducen a

  • Las variables locales no se pueden compartir : las variables locales son independientes cada vez que se llama al método, por lo que los datos de ejecución () de cada hilo son independientes, no datos compartidos.
  • Las variables de instancia de diferentes objetos no se comparten : las variables de instancia de diferentes objetos de instancia son independientes.
  • las variables estáticas se comparten
  • Las variables de instancia del mismo objeto se comparten

Resumen: surgen problemas de seguridad de subprocesos debido a las siguientes condiciones

  1. ejecución de subprocesos múltiples
  2. compartir datos
  3. Múltiples declaraciones operan en datos compartidos

Solución al problema de seguridad de subprocesos

inserte la descripción de la imagen aquí
Método de sincronización : la palabra clave sincronizada modifica directamente el método, lo que indica que solo un subproceso puede ingresar a este método al mismo tiempo, y otros subprocesos están esperando afuera.

public synchronized void method(){
    
    
    可能会产生线程安全问题的代码
}

Bloque de código sincronizado : la palabra clave sincronizada se puede usar delante de un determinado bloque, lo que indica que solo los recursos de este bloque son mutuamente excluyentes.

synchronized(同步锁){
    
    
     需要同步操作的代码
}

Bloquear selección de objetos

preferí estoseguido porclase.clasepuede también ser" "objeto de cadena vacía

Objeto de bloqueo de sincronización:

  • Los objetos de bloqueo pueden ser de cualquier tipo.
  • Múltiples objetos de hilo usan el mismo bloqueo.

El objeto de bloqueo del bloque de código sincronizado

  • En el bloque de código estático: use el objeto Clase de la clase actual
  • En bloques de código no estático: es costumbre considerar esto primero, pero preste atención a si lo mismo esto

El alcance del bloqueo es demasiado pequeño: no puede resolver problemas de seguridad y todas las declaraciones que operan en recursos compartidos deben sincronizarse.

El alcance del bloqueo es demasiado grande: porque una vez que un subproceso toma el bloqueo, otros subprocesos solo pueden esperar, por lo que el alcance es demasiado grande, la eficiencia se reducirá y los recursos de la CPU no se pueden usar de manera razonable.

Temas de seguridad de subprocesos del patrón de diseño Singleton

1. El estilo chino hambriento no tiene problemas de seguridad de hilos

Estilo chino hambriento: crea objetos tan pronto como aparezcas

2. Problemas de seguridad de subprocesos de estilo perezoso

public class Singleton {
    
    
    private static Singleton ourInstance;

    public static Singleton getInstance() {
    
    
        //一旦创建了对象,之后再次获取对象,都不会再进入同步代码块,提升效率
        if (ourInstance == null) {
    
    
            //同步锁,锁住判断语句与创建对象并赋值的语句
            synchronized (Singleton.class) {
    
    
                if (ourInstance == null) {
    
    
                    ourInstance = new Singleton();
                }
            }
        }
        return ourInstance;
    }

    private Singleton() {
    
    
    }
}

Esperando el mecanismo de activación

Cuando un subproceso cumple una determinada condición, entra en estado de espera ( esperar() / esperar(tiempo) ), espera a que otros subprocesos ejecuten su código especificado y luego lo activa ( notificar() ); o puede especificar la espera time , espere a que la hora se despierte automáticamente; cuando hay varios subprocesos en espera, si es necesario, puede usar notificarTodos() para despertar todos los subprocesos en espera. wait/notify es un mecanismo de coordinación entre hilos.

  1. esperar: el subproceso ya no está activo, ya no participa en la programación y entra en el conjunto de espera, por lo que no desperdiciará recursos de CPU y no competirá por bloqueos. El estado del subproceso en este momento es ESPERANDO o TIMED_WAITING. También espera a que otros subprocesos realicen una acción especial , es decir, " notificar " o cuando se acabe el tiempo de espera, el subproceso que espera en este objeto se libera del conjunto de espera y vuelve a ingresar a la cola de envío (cola lista)) medio
  2. notificar: seleccione un hilo en el conjunto de espera del objeto notificado para liberar;
  3. notificarTodos: libera todos los subprocesos en el conjunto de espera del objeto notificado.

Nota:
Después de que se active el subproceso notificado, es posible que no pueda reanudar la ejecución de inmediato, porque el lugar donde se interrumpió estaba en el bloque de sincronización, y en este momento ya no mantiene el bloqueo, por lo que debe intentar adquiera el bloqueo nuevamente (probablemente frente a otra competencia de Thread), solo después del éxito se puede reanudar la ejecución en el lugar después de que se llamó originalmente al método de espera.

Resumido de la siguiente manera:

  • Si se puede adquirir el bloqueo, el subproceso cambia del estado EN ESPERA al estado EJECUTABLE (ejecutable);
  • De lo contrario, el subproceso cambia del estado EN ESPERA al estado BLOQUEADO (en espera de bloqueo)

Los detalles a los que se debe prestar atención al llamar a los métodos de espera y notificación

  1. El mismo objeto de bloqueo debe llamar al método de espera y al método de notificación. Porque: el objeto de bloqueo correspondiente puede activar el hilo después de que el método de espera haya llamado con el mismo objeto de bloqueo mediante notificación.
  2. El método de espera y el método de notificación pertenecen a los métodos de la clase Object. Porque: el objeto de bloqueo puede ser cualquier objeto y la clase de cualquier objeto hereda la clase Object.
  3. El método de espera y el método de notificación deben usarse en un bloque de código de sincronización o una función de sincronización, y estos dos métodos deben llamarse a través del objeto de bloqueo.

Liberación de la operación de bloqueo y punto muerto

1. La operación de desbloqueo

  • La ejecución del método de sincronización y el bloque de código de sincronización del subproceso actual finaliza.

  • Se produce un error o una excepción no controlados en el bloque de código de sincronización o en el método de sincronización del subproceso actual, lo que hace que el subproceso actual finalice de forma anómala.

  • El subproceso actual ejecuta el método wait() del objeto de bloqueo en el bloque de código de sincronización y el método de sincronización, el subproceso actual se suspende y se libera el bloqueo.

2. punto muerto

Distintos subprocesos bloquean el objeto del monitor de sincronización requerido por la otra parte y no lo liberan.Cuando están esperando que la otra parte se rinda primero, se forma un interbloqueo de subprocesos. Una vez que ocurre un interbloqueo, el programa completo no será anormal ni dará indicaciones, pero todos los subprocesos se bloquearán y no podrán continuar.

3. Pregunta de la entrevista: la diferencia entre los métodos sleep() y wait()

(1) sleep() no libera el bloqueo, wait() libera el bloqueo

(2) dormir () especifica el tiempo de suspensión, esperar () puede especificar el tiempo o esperar indefinidamente hasta notificar o notificar a todos

(3) sleep() es un método estático declarado en la clase Thread, y el método de espera se declara en la clase Object

Debido a que llamamos al método wait (), el objeto de bloqueo llama al método, y el tipo de objeto de bloqueo es cualquier tipo de objeto. Luego, los métodos que desea que tenga cualquier tipo de objeto solo se pueden declarar en la clase Object.

práctica

Se requieren dos hilos para imprimir letras al mismo tiempo, y cada hilo puede imprimir 3 letras continuamente. Dos hilos imprimen alternativamente, un hilo imprime la forma minúscula de la letra y un hilo imprime la forma mayúscula de la letra, pero las letras son consecutivas. Después de que la letra pase a la z, vuelva a la a.

inserte la descripción de la imagen aquí

public class PrintLetterDemo {
    
    
	public static void main(String[] args) {
    
    
		// 2、创建资源对象
		PrintLetter p = new PrintLetter();

		// 3、创建两个线程打印
		new Thread("小写字母") {
    
    
			public void run() {
    
    
				while (true) {
    
    
					p.printLower();
					try {
    
    
						Thread.sleep(1000);// 控制节奏
					} catch (InterruptedException e) {
    
    
						e.printStackTrace();
					}
				}
			}
		}.start();

		new Thread("大写字母") {
    
    
			public void run() {
    
    
				while (true) {
    
    
					p.printUpper();
					try {
    
    
						Thread.sleep(1000);// 控制节奏
					} catch (InterruptedException e) {
    
    
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
}

// 1、定义资源类
class PrintLetter {
    
    
	private char letter = 'a';

	public synchronized void printLower() {
    
    
		for (int i = 1; i <= 3; i++) {
    
    
			System.out.println(Thread.currentThread().getName() + "->" + letter);
			letter++;
			if (letter > 'z') {
    
    
				letter = 'a';
			}
		}
		this.notify();
		try {
    
    
			this.wait();
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		}
	}

	public synchronized void printUpper() {
    
    
		for (int i = 1; i <= 3; i++) {
    
    
			System.out.println(Thread.currentThread().getName() + "->" + (char) (letter - 32));
			letter++;
			if (letter > 'z') {
    
    
				letter = 'a';
			}
		}
		this.notify();
		try {
    
    
			this.wait();
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		}
	}
} 

Supongo que te gusta

Origin blog.csdn.net/qq_52370789/article/details/129367778
Recomendado
Clasificación