La creación y uso de hilos en Java, métodos comunes de la clase Thread

1. ¿Qué son los procesos y los hilos?

1.1 Significado

1.1.1 Proceso

Un proceso es una instancia de un programa que se está ejecutando. En un sistema operativo, un proceso representa un programa en ejecución, que incluye código de programa, datos y recursos del sistema necesarios para la ejecución del programa.

El más intuitivo es nuestro administrador de tareas:

Cada aplicación en el Administrador de tareas es un proceso.

1.1.2 Hilos

Un hilo es una unidad de ejecución en un proceso. Un proceso puede contener múltiples subprocesos, cada subproceso tiene una ruta de ejecución independiente y puede realizar tareas específicas de forma independiente, y todos los subprocesos en un proceso comparten los recursos de este proceso.

Como el siguiente ejemplo. Proceso: Todo el proceso operativo de un restaurante puede considerarse como un proceso. Incluye todos los recursos y actividades como cocina, sala de restaurante, camareros, cocineros, clientes, etc. Hilo: en un restaurante, se puede pensar en un camarero como un hilo. Hay muchos camareros, que se encargan de recibir a los clientes, registrar los pedidos, pasar los menús, servir los platos y otras tareas. El camarero es una unidad de ejecución en el proceso del restaurante, y comparte los mismos recursos con otros camareros, como la mesa, la cocina, los ingredientes, etc. del restaurante. Varios servidores pueden ejecutar tareas en paralelo para mejorar la eficiencia.

1.1.3 La diferencia entre hilo y proceso

El nacimiento del proceso es realizar la ejecución concurrente del programa. La llamada concurrencia significa que en el sistema operativo hay varios programas en un período de tiempo entre el inicio y la ejecución y ejecución, y todos estos programas se ejecutan en el mismo procesador.

El propósito del nacimiento de subprocesos es reducir la sobrecarga de tiempo y espacio de los programas durante la ejecución concurrente, para que el sistema operativo tenga una mejor concurrencia.

  • Espacio de direcciones : los subprocesos comparten el espacio de direcciones del proceso, mientras que los procesos tienen espacios de direcciones independientes.
  • Recursos : los hilos comparten los recursos del proceso, como memoria, E/S, CPU, etc., mientras que los recursos entre procesos son independientes.
  • Robustez : el multiproceso es más fuerte que el multihilo. Después de que un proceso falla, no afectará a otros procesos en modo protegido, pero si un hilo falla, todo el proceso morirá.
  • Proceso de ejecución : cada proceso independiente tiene una entrada en ejecución del programa, una secuencia de ejecución secuencial y una salida del programa, y ​​la sobrecarga de ejecución es alta. Sin embargo, los subprocesos no se pueden ejecutar de forma independiente y deben depender del programa de aplicación.El programa de aplicación proporciona control de ejecución de múltiples subprocesos y la sobrecarga de ejecución es pequeña.
  • Concurrencia : ambos se pueden ejecutar simultáneamente.
  • Al cambiar : cuando se cambia el proceso, consume muchos recursos y tiene poca eficiencia. Entonces, cuando se trata de cambios frecuentes, es mejor usar subprocesos que procesos.
  • Otros : El proceso es la unidad básica de asignación de recursos del sistema operativo y el subproceso es la unidad básica de programación del sistema operativo .

1.2 PCB y TCB

PCB del bloque de control de procesos (Process Control Block), que es una estructura de datos utilizada para describir y controlar el funcionamiento del proceso . Es el único signo de la existencia del proceso y también es una base importante para que el sistema operativo gestione el proceso.

TCB (Bloque de control de subprocesos) es la abreviatura de Bloque de control de subprocesos, que es una estructura de datos utilizada en el sistema operativo para describir y controlar la ejecución de subprocesos .

Su relación:

Los identificadores de procesos, estados, prioridades, registros, contadores de programas, punteros de pila, listas de recursos, mecanismos de sincronización y comunicación y otra información se almacenan en la PCB. El PCB es el único signo de la existencia de un proceso, y también es la base para realizar la conmutación y programación de procesos.

El TCB es similar al PCB.El TCB también almacena identificadores de subprocesos, estado, prioridad, registros, contadores de programa, punteros de pila, blindaje de señal y otra información. TCB es el único signo de la existencia de subprocesos y también es la base para el cambio y la programación de subprocesos.

2. Varias formas de crear hilos

Thread es un concepto en el sistema operativo. El kernel del sistema operativo implementa un mecanismo como subprocesos y proporciona algunas API a la capa de usuario para que los usuarios las utilicen. La clase Thread en la biblioteca estándar de Java se puede considerar como una abstracción y encapsulación adicional de la API proporcionada por el sistema operativo.

2.1 Heredando la clase Thread

(1) Heredar subproceso para crear una clase de subproceso.

class MyThread extends Thread{
    //重写run方法,run 表示这个线程需要执行的任务
    @Override
    public void run() {
        System.out.println("这个线程需要执行的任务");
    }
}

(2) Cree una instancia de MyThread.

//创建一个 MyThread 实例
MyThread myThread = new MyThread();

(3) Llame al método start() y el hilo comienza a ejecutarse.

//start 方法表示这个线程开始执行,注意,这里不是调用 run()方法
myThread.start();

2.2 Implementar la interfaz Runnable

(1) Implementar la interfaz Runnable.

//通过 实现 Runnable 接口来创建一个线程
class MyRunnable implements Runnable{

    //需要重写run方法
    @Override
    public void run() {
        System.out.println("这个线程需要执行的任务");
    }
}

(2) Cree una instancia de Thread.

//创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为参数。
Thread thread = new Thread(new MyRunnable());

(3) Llame al método start() y el hilo comienza a ejecutarse.

//线程开始运行
thread.start();

2.3 Otras deformaciones

  • Una clase interna anónima crea un objeto de subclase Thread.
Thread thread1 = new Thread(){
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Thread 子类对象");
    }
};
  • Las clases internas anónimas crean objetos de subclase Runnable.
Thread thread2 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Runnable 子类对象");
    }
});
  • Las expresiones lambda crean objetos de subclase Runnable.
Thread thread3 = new Thread(()-> {
    System.out.println("使用匿名类创建 Thread 子类对象");
});

3. Métodos comunes de la clase Thread

3.1 Métodos de construcción comunes de Thread

método ilustrar
Hilo() crear objeto hilo
Subproceso (objetivo ejecutable) Cree un objeto de hilo usando un objeto Runnable
Subproceso (nombre de la cadena) Cree un objeto de hilo y asígnele un nombre
Subproceso (objetivo ejecutable, nombre de cadena) Use el objeto Runnable para crear un objeto de subproceso y asígnele un nombre
Thread thread = new Thread("小明"){
    @Override
    public void run(){
        while(true){
        	System.out.println("我是小明");
    	}   
    }
};

El nombre de este hilo es "Xiao Ming", ¿cómo verificar el nombre de este hilo? En el Kit de desarrollo de Java (JDK), puede usar jconsole para monitorear subprocesos.

  1. Abra su ruta jdk, aquí está C:\Program Files\Java\jdk1.8.0_31\bin, abra la herramienta jconsole en esta ruta.
  2. Ejecute el código anterior y luego proceda de la siguiente manera:

Puede ver que hay un hilo llamado "Xiao Ming", que es el hilo que creamos.

3.2 Método para obtener el atributo Thread

Atributos método de acceso
IDENTIFICACIÓN obtenerId()
nombre obtenerNombre()
estado obtenerEstado()
prioridad obtenerPrioridad()
Si hilo de fondo esDaemon()
ya sea para sobrevivir está vivo()
¿Está interrumpido? está interrumpido ()
  • ID es el identificador único del hilo, no se repetirán hilos diferentes.
  • nombre es el nombre del hilo.
  • El estado representa una situación en la que se encuentra actualmente el subproceso, como bloqueo, ejecución, etc.
  • Los subprocesos con mayor prioridad tienen más probabilidades de programarse.
  • Acerca del subproceso en segundo plano: Daemon Thread es un subproceso que proporciona servicios en segundo plano durante la ejecución del programa. A diferencia de los subprocesos en primer plano (también conocidos como subprocesos de usuario), los subprocesos en segundo plano no impiden la terminación del programa. Los subprocesos en segundo plano finalizan automáticamente cuando finalizan todos los subprocesos en primer plano (subprocesos de usuario), incluso si aún no han terminado de ejecutarse.
  • Si está vivo, es decir, un simple entendimiento, es si el método de ejecución está terminado.

3.3 El método para iniciar el hilo start()

Se ha demostrado en el artículo anterior y hemos visto cómo crear un objeto de subproceso anulando el método de ejecución, pero la creación de un objeto de subproceso no significa que el subproceso comience a ejecutarse. Anule el método de ejecución para proporcionar al subproceso una lista de instrucciones sobre qué hacer. Solo llamando al método start() se puede ejecutar el subproceso de forma independiente, y solo llamando al método start se puede crear un subproceso en la parte inferior del sistema operativo.

3.4 subproceso dormir dormir()

método ilustrar
el sueño vacío estático público (milis largo) arroja una excepción interrumpida Dormir el hilo actual durante milisegundos
el sueño vacío estático público (millis largos, nanos int) lanza una excepción interrumpida Puede dormir con mayor precisión

El siguiente código:

public static void main(String[] args) throws InterruptedException {
    long begin = System.currentTimeMillis();
    Thread.sleep(3000);//睡眠当前线程
    long end = System.currentTimeMillis();
    System.out.println(end - begin);
}

resultado:

Parte de su contenido más detallado se presenta más adelante.

3.5 Hilos interrumpidos

La interrupción aquí no significa interrupción inmediata Veamos el siguiente caso para más detalles.

Hay dos subprocesos aquí, un subproceso se usa para interrumpir el otro subproceso y el método Subproceso no se usa aquí.

public class Main {
    
    public static volatile boolean flag = true;
    
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(){
            @Override
            public void run(){
                // flag 是中断标记
                while(flag){
                    try {
                        Thread.sleep(500);//睡眠
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Hi");
                }
            }
        };
        //开启线程
        thread.start();

        //中断线程
        Thread.sleep(3000); //睡眠 3 秒
        flag = false;//更改标记
        System.out.println("开始中断线程");
    }
}

(La función de volátil no se presentará aquí y se seguirá actualizando más adelante).

resultado:

Encontrará que la interrupción aquí es para cambiar el valor del bit de bandera, y hay un retraso aquí, y el subproceso solo se puede terminar cuando finaliza la suspensión.

Entonces puede usar el método provisto por Thread, a saber: isInterrupted() o interrumpido(), su ventaja es que pueden despertar el hilo durmiente inmediatamente, vea el siguiente caso.

método ilustrar
interrupción de vacío público () Interrumpir el hilo asociado con el objeto, si el hilo está bloqueado, se notificará de manera anormal, de lo contrario, se establecerá el bit de bandera
booleano estático público interrumpido () Determine si el bit indicador de interrupción del subproceso actual está establecido y borre el bit indicador después de llamar
booleano público está interrumpido () Determine si el bit indicador del subproceso asociado con el objeto está establecido y si el bit indicador no se borra después de llamar

Los detalles de las banderas aquí se describen más adelante.

public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(){
            @Override
            public void run(){
                // flag 是中断标记
                while(!Thread.currentThread().isInterrupted()){
                    try {
                        Thread.sleep(500);//随眠
                    } catch (InterruptedException e) {
                        e.printStackTrace();//打印报错信息
                    }
                    System.out.println("Hi");
                }
            }
        };
        //开启线程
        thread.start();

        Thread.sleep(3000); //睡眠 3 秒
        thread.interrupt();//中断 thread 线程,把标记位置为true。
        System.out.println("开始中断线程");
    }

¡Explique aquí! Thread.currentThread().isInterrupted(), Thread.currentThread() es para devolver la referencia del objeto de hilo actual, que es similar a esto:

método ilustrar
Subproceso estático público subproceso actual(); Devuelve una referencia al objeto hilo actual

isInterrupted(): devuelve verdadero si el subproceso actual se interrumpe; de ​​lo contrario, devuelve falso.

La interrupción aquí debe verse de acuerdo con el bit indicador de interrupción. El bit indicador de interrupción (indicador de interrupción) es uno de los estados internos del subproceso y, de hecho, existe en la estructura de datos interna del subproceso. **Específicamente, el bit indicador de interrupción es una variable miembro del objeto de subproceso, que se utiliza para indicar el estado de interrupción del subproceso. **Este indicador se inicializa en falso cuando se crea el subproceso. Cuando se llama al método interrupt() del subproceso, el indicador de interrupción se establecerá en verdadero, lo que indica que el subproceso está interrumpido.

Thread.currentThread().isInterrupted(), aquí indica si el subproceso actual está interrumpido y devuelve verdadero si se interrumpe; de ​​lo contrario, devuelve falso. Finalmente, agregue un !

Finalmente, echemos un vistazo a los resultados. Tenga en cuenta que hemos llamado a thread.interrupt() arriba, por lo que la expectativa es que sleep arroje una excepción y luego finalice el ciclo (fin del hilo),

resultado:

¿Por qué el programa no termina aquí? Analicémoslo lentamente, es obvio que la condición del ciclo while es verdadera si no ha terminado, entonces el valor de Thread.currentThread().isInterrupted() es falso, lo que significa que el hilo no está en un estado interrumpido . ¿Qué? ¿No hemos llamado ya al método interrupt()?

La razón es: cuando el subproceso está bloqueado, como llamar al método sleep (), join () o wait (), si se recibe una solicitud de interrupción, ¡estos métodos lanzarán InterruptedException y borrarán el estado de interrupción ! , el estado de interrupción clara aquí es establecer la posición de la bandera en falso, lo que indica que el subproceso no se ha interrumpido.

¿Cómo resolver este problema? Simplemente agregue un descanso directamente a la captura.

public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(){
            @Override
            public void run(){
                // flag 是中断标记
                while(!Thread.currentThread().isInterrupted()){
                    try {
                        Thread.sleep(500);//随眠
                    } catch (InterruptedException e) {
                        //这里进行善后处理

                        break;
                    }
                    System.out.println("Hi");
                }
            }
        };
        //开启线程
        thread.start();

        //中断线程
        Thread.sleep(3000); //睡眠 3 秒
        thread.interrupt();//中断 thread 线程
        System.out.println("开始中断线程");
    }

¿Cuáles son las ventajas de hacer algo así? El subproceso puede manejar la solicitud de interrupción de acuerdo con su propia lógica, como finalizar el ciclo, cerrar el recurso o ignorarlo.

Resumen: condiciones previas, interrupción de llamada ();

  1. Si el subproceso se bloquea y suspende debido a métodos de llamada como esperar/unirse/dormir, se notificará en forma de InterruptedException y se restablecerá el indicador de interrupción. La finalización del subproceso en este momento depende de la forma en que se escribe el código en el catch. Puede optar por ignorar esta excepción, o puede saltar fuera del bucle para finalizar el hilo.
  2. Si no hay métodos como esperar/unirse/dormir,
  3. Thread.interrupted() Determina si el indicador de interrupción del subproceso actual está establecido y restablece el indicador de interrupción después de juzgar.
  4. Thread.currentThread().isInterrupted() Determina si el indicador de interrupción del subproceso actual está establecido y no restablece el indicador de interrupción.

3.6 Esperando la unión del hilo()

A veces, necesitamos esperar a que un subproceso complete su trabajo antes de continuar con el siguiente paso.

método describir
unirse() Espere a que el hilo se complete
unirse (milis largo) Espere hasta el número especificado de milisegundos, si el subproceso no completa la ejecución dentro de este tiempo, el subproceso actual continuará ejecutándose
unirse (long millis, int nanos) Espere hasta el número especificado de milisegundos y nanosegundos. Si el subproceso no completa la ejecución dentro de este tiempo, el subproceso actual continuará ejecutándose
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(){
            @Override
            public void run(){
                for (int i = 0; i < 9; i++) {
                    System.out.println("thread");
                }
            }
        };

        thread.start();//开启线程

        System.out.println("join()前");
        try {
            thread.join();//先等 thread 线程执行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join()后");
    }
}

Si el subproceso ha terminado de ejecutarse antes de join(), join() no se bloqueará.

Estos métodos join() proporcionan un mecanismo de ejecución cooperativa entre subprocesos, lo que permite que un subproceso espere a que otro subproceso se complete antes de continuar con la ejecución. Esto es útil para escenarios que requieren un orden de ejecución de subprocesos o dependencias entre subprocesos.

Supongo que te gusta

Origin blog.csdn.net/mxt51220/article/details/131361893
Recomendado
Clasificación