Subprocesos múltiples: ciclo de vida del subproceso y control de estado

1. Ciclo de vida del hilo

 El ciclo de vida de un hilo es el proceso desde la creación hasta la muerte de un hilo. Con respecto al ciclo de vida de los subprocesos en Java, primero mire la imagen más clásica a continuación:

0b06a75702744b73afcf8a9584a284f7.png

Cuando se crea e inicia un hilo, no entra en el estado de ejecución tan pronto como se inicia ni permanece en el estado de ejecución. En el ciclo de vida de un hilo , pasa por cinco estados diferentes : Nuevo , Listo , Ejecutable , En ejecución , Bloqueado y Muerto . Especialmente cuando se inicia un subproceso, no siempre puede "ocupar" la CPU para ejecutarse solo, por lo que la CPU necesita cambiar entre varios subprocesos, por lo que el estado del subproceso cambiará entre ejecución y bloqueo varias veces.

1) Nuevo estado ( Nuevo )

Después de usar la nueva palabra clave y la clase Thread o su subclase para crear un objeto thread, el objeto thread está en un estado recién creado. El subproceso en el estado recién creado tiene su propio espacio de memoria y ingresa al estado listo (Ejecutable) llamando al método de inicio.

2) Estado listo ( ejecutable )

El subproceso en estado listo ya tiene las condiciones de ejecución (es decir, está calificado para ejecutarse en la CPU), pero aún no se le han asignado los derechos de ejecución de la CPU y está en la "cola de subproceso listo", esperando el sistema le asigne una CPU. El estado listo no es el estado de ejecución. Cuando el sistema selecciona un objeto Thread en espera de ejecución, entrará en el estado de ejecución. Una vez que obtiene la CPU, el subproceso entra en estado de ejecución y automáticamente llama a su propio método de ejecución.

3) Estado de ejecución ( en ejecución )

Si la CPU programa un subproceso en estado listo, cambiará del estado listo al estado en ejecución y ejecutará las tareas en el método run().

El estado de ejecución puede cambiar al estado de bloqueo, al estado de preparación y al estado de muerte.

Si el subproceso pierde recursos de la CPU, cambiará del estado de ejecución al estado listo y esperará a que el sistema asigne recursos nuevamente. También puede llamar al método yield() en un subproceso en estado de ejecución, y este abandonará los recursos de la CPU y estará listo nuevamente.

4) Estado bloqueado ( Bloqueado )

En algunas circunstancias especiales, cuando alguien suspende o realiza operaciones de entrada y salida, renuncia a los derechos de ejecución de la CPU e interrumpe temporalmente su propia ejecución, entrando así en un estado de bloqueo y no tendrá la oportunidad de ser llamado nuevamente por la CPU. hasta que entre en el estado listo para entrar en el estado en ejecución.

Según los diferentes motivos del bloqueo, el estado de bloqueo se puede dividir en tres tipos:

1) Esperando bloqueo: el hilo en el estado de ejecución ejecuta el método de espera (), lo que hace que este hilo entre en el estado de espera de bloqueo.

           Cuando se llaman a métodos como notify() o notifyAll(), el hilo volverá a entrar en el estado listo.

2) Bloqueo sincrónico: si el subproceso no logra adquirir el bloqueo de sincronización (porque el bloqueo está ocupado por otros subprocesos), entrará en el estado de bloqueo de sincronización.

Cuando el bloqueo de sincronización se adquiere con éxito, el subproceso volverá al estado listo.

3) Otro bloqueo: cuando se llama al subproceso sleep () o join () o se emite una solicitud de E/S, el subproceso entrará en estado de bloqueo.

           Cuando el estado de suspensión () se agota, join () espera a que el subproceso finalice o se agote el tiempo de espera, o se complete el procesamiento de E/S, el subproceso volverá a entrar en el estado listo.

5) Estado muerto ( Muerto )

Cuando el hilo termina de ejecutar el método run() o sale del método run() debido a una excepción, el hilo finaliza su ciclo de vida. Además, si el hilo ejecuta el método de interrupción () o parada (), también entrará en el estado de muerte al salir de forma anormal.

2. Control del estado del hilo.

Métodos proporcionados en Java para controlar el estado del hilo:

Métodos de control directo: inicio(), interrupción(), unión(), suspensión(), rendimiento()

Métodos de control indirecto: setDaemon(), setPriority()

2.1 Hilo de suspensión

Si necesitamos hacer que el hilo que se está ejecutando actualmente se detenga por un período de tiempo y entre en el estado de bloqueo. Después del tiempo especificado, el estado de bloqueo se desbloquea y entra en el estado listo, podemos llamar al método de suspensión del hilo. Desde la API, Podemos ver que hay dos métodos para dormir: la forma sobrecargada, pero se usa exactamente de la misma manera.

21e7a119b96e44c1b6831fd3afa4d633.png

Por ejemplo, queremos hacer que el hilo principal duerma cada 1000 milisegundos y luego imprimir el número:

[Ejemplo] Imprimir un número cada 1000 milisegundos

Prueba de clase pública {

    público estático vacío principal (String [] argumentos) {

        para ( int i = 1; i < 10; i++) {

            Sistema. fuera .println(i);

            prueba {

                Thread.sleep ( 1000 ); // Dormir el hilo principal durante 1 segundo

            } captura (Excepción interrumpida e) {

                e.printStackTrace();

            }

        }

    }

}

Al ejecutar el código, podemos ver claramente que los números impresos están ligeramente espaciados en el tiempo.

Precauciones:

        1) dormir es un método estático. Es mejor no llamarlo con un objeto de instancia de Thread, porque siempre duerme el hilo que se está ejecutando actualmente, no el objeto de hilo que lo llama. Solo es válido para el objeto de hilo en ejecución. estado.

        2) Después de usar el método de suspensión, el hilo ingresa al estado de bloqueo. Solo cuando finaliza el tiempo de suspensión, volverá a ingresar al estado listo. La transición del estado listo al estado de ejecución está controlada por el sistema, y ​​nosotros no puede interferir con precisión, por lo que si se llama a Thread.sleep(1000) para hacer que el hilo entre en suspensión durante 1 segundo, el resultado puede ser mayor que 1 segundo.

2.2 Prioridad del hilo

Cada subproceso tiene un atributo de prioridad al ejecutarse: los subprocesos con alta prioridad pueden obtener más oportunidades de ejecución, mientras que los subprocesos con baja prioridad pueden obtener menos oportunidades de ejecución. De manera similar a la suspensión del subproceso, la prioridad del subproceso aún no puede garantizar el orden de ejecución del subproceso. Sin embargo, los subprocesos con alta prioridad tienen una mayor probabilidad de obtener recursos de la CPU y los subprocesos con baja prioridad no tienen ninguna posibilidad de ejecutarse.

La clase Thread proporciona los métodos setPriority(int newPriority) y getPriority() para establecer y devolver la prioridad de un hilo específico. El parámetro del método setPriority es un número entero, que se puede establecer en un número entero entre [1-10]. Cuanto mayor sea el valor, mayor será la prioridad. También puede utilizar las tres constantes estáticas proporcionadas por la clase Thread:

público final estático int MIN_PRIORITY = 1;

público final estático int NORM_PRIORITY = 5;

público final estático int MAX_PRIORITY = 10;

[Ejemplo] Pruebe la ejecución de la prioridad del subproceso

/**

 * Clase de hilo personalizada

 */

clase TestThread extiende Thread {

    hilo de prueba público () {}

    TestThread público (nombre de cadena, int pro) {

        super (nombre); //Establece el nombre del hilo

        this .setPriority(pro); // Establece la prioridad del hilo

    }

    @Anular

    ejecución pública vacía () {

        para ( int i = 0; i < 10; i++) {

            System.out .println ( this .getName() + "hilo" + i + "¡ejecución del hilo!");

        }

    }

}

/**

 * Clase de prueba

 */

Prueba de clase pública {

    público estático vacío principal (String [] argumentos) {

        new TestThread("Avanzado", 10).start();

        new TestThread("bajo nivel", 1).start();

    }

}

Al ejecutar el programa, puede ver en los resultados que, en circunstancias normales, el subproceso de alto nivel completa la ejecución primero.

2.3 Concesión de hilo

yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态的方法,它也可以让当前正在执行的线程暂停,让出CPU资源给其它的线程。但是和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态。yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,完全可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行。

实际上,当某个线程调用了yield()方法暂停之后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能获得执行的机会,当然,只是有可能,因为我们不可能精确的干涉CPU调度线程。

【示例】线程让步的使用

/**

 * 自定义线程类

 */

class TestThread extends Thread {

    public TestThread() {}

    public TestThread(String name, int pro) {

        super(name); // 设置线程名字

        this.setPriority(pro); // 设置线程的优先级

    }

    @Override

    public void run() {

        for (int i = 0; i < 5; i++) {

            System.out.println(this.getName() + "线程第" + i + "次执行!");

            Thread.yield(); // 线程让步

        }

    }

}

/**

 * 测试类

 */

public class Test {

    public static void main(String[] args) {

        new TestThread("高级", 10).start();

        new TestThread("低级", 1).start();

    }

}

关于sleep()方法和yield()方的区别如下:

  1. sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态。
  2. sleep方法声明抛出了InterruptedException,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。
  1. sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行。

2.4 线程合并

线程的合并就是:线程A在运行期间,可以调用线程B的join()方法,这样线程A就必须等待线程B执行完毕后,才能继续执行。

应用场景是当一个线程必须等待另一个线程执行完毕才能执行时,Thread类提供了join方法来完成这个功能,注意,它不是静态方法。

线程合并有三个重载的方法:

12871d6b948a401aa82fd398f9ffa871.png

【示例】线程合并的使用

/**

 * 自定义线程类

 */

class TestThread extends Thread {

    public TestThread() {}

    public TestThread(String name, int pro) {

        super(name); // 设置线程名字

        this.setPriority(pro); // 设置线程的优先级

    }

    @Override

    public void run() {

        for (int i = 0; i < 30; i++) {

            System.out.println(this.getName() + "线程第" + i + "次执行!");

        }

    }

}

/**

 * 测试类

 */

public class Test {

    public static void main(String[] args) {

        TestThread th = new TestThread();

        th.start();

        try {

            // 等待th线程执行任务完毕之后,再执行主线程中的任务

            th.join();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        // 主线程任务

        for (int i = 0; i < 30; i++) {

            String name = Thread.currentThread().getName();

            System.out.println(name + "线程第" + i + "次执行!");

        }

    }

}

在这个例子中,在主线程中调用th.join(); 就是将主线程加入到th子线程后面等待执行。

2.5 守护线程

守护线程与普通线程写法上基本没啥区别,调用线程对象的方法setDaemon(true),就可以把该线程标记为守护线程。

当普通线程(前台线程)都全部执行完毕,也就是当前在运行的线程都是守护线程时,Java虚拟机(JVM)将退出。另外,setDaemon(true)方法必须在启动线程前调用,否则抛出IllegalThreadStateException异常。

【示例】线程合并的使用

// 前台线程

class CommonThread extends Thread {

    @Override

    public void run() {

        for (int i = 0; i < 5; i++) {

            System.out.println("前台线程第" + i + "次执行!");

        }

    }

}

// 后台线程或守护线程

class DaemonThread extends Thread {

    int i = 0;

    @Override

    public void run() {

        while(true) {

            System.out.println("后台线程第" + i++ + "次执行!");

        }

    }

}

//测试类

Prueba de clase pública {

    público estático vacío principal (String [] argumentos) {

        CommonThread ct = nuevo CommonThread();

        DaemonThread dt = nuevo DaemonThread();

        ct.start();

        dt.setDaemon( true ); // Establece dt como un hilo de demonio

        dt.start();

    }

}

Ejecute el código anterior y podrá ver en los resultados de la ejecución: se garantiza que el subproceso en primer plano completará la ejecución y el subproceso en segundo plano sale antes de que complete la ejecución.

El propósito del hilo demonio:

Los subprocesos de demonio generalmente se usan para realizar algunas tareas en segundo plano, como reproducir música de fondo cuando la aplicación se está ejecutando, realizar una revisión gramatical automática, guardar automáticamente, etc. en un editor de texto. La recolección de basura de Java también es un hilo de demonio. La ventaja de proteger la línea es que no necesitas preocuparte por su final . Por ejemplo, si desea reproducir música de fondo mientras su aplicación se está ejecutando, si el hilo que reproduce música de fondo está configurado como un hilo que no es demonio, cuando el usuario solicite salir, no solo debe salir el hilo principal, sino también se debe notificar a la música de fondo para que se reproduzca. El hilo se cierra; si está configurado como un hilo demonio, no es necesario.

 

 

Supongo que te gusta

Origin blog.csdn.net/shengshanlaolin_/article/details/127476522
Recomendado
Clasificación