El hilo es la unidad básica de operación del sistema operativo. Está encapsulado en un proceso. Un proceso puede contener varios hilos. Incluso si no creamos un hilo manualmente, el proceso tendrá un hilo predeterminado ejecutándose.
Para la JVM, cuando escribimos un programa de un solo subproceso para ejecutar, hay al menos dos subprocesos ejecutándose en la JVM, uno es el programa que creamos y el otro es la recolección de basura.
Información básica del hilo
Podemos obtener información sobre el hilo actual a través del método Thread.currentThread() y modificarlo.
Veamos el siguiente código:
Thread.currentThread().setName("Prueba");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
nombre = Thread.currentThread().getName();
prioridad = Thread.currentThread().getPriority();
nombreGrupo = Thread.currentThread().getThreadGroup().getName();
isDaemon = Thread.currentThread().isDaemon();
System.out.println("Nombre del hilo:" + nombre);
System.out.println("Prioridad:" + prioridad);
GroupName , cada subproceso estará en un grupo de subprocesos de forma predeterminada. También podemos crear explícitamente un grupo de subprocesos. Un grupo de subprocesos también puede contener grupos de subprocesos, de modo que los subprocesos y los grupos de subprocesos formen una estructura de árbol.
Nombre , cada hilo tendrá un nombre. Si no se especifica explícitamente, la regla de nombre es "Thread-xxx".
Prioridad , cada subproceso tendrá su propia prioridad y la forma en que la JVM maneja la prioridad es "preventiva". Cuando la JVM encuentra un subproceso con alta prioridad, lo ejecuta inmediatamente para varios subprocesos con la misma prioridad y la JVM los sondea; La prioridad de los subprocesos de Java varía de 1 a 10, siendo el valor predeterminado 5. La clase Thread define dos constantes: MIN_PRIORITY y MAX_PRIORITY para representar las prioridades más alta y más baja.
Podemos mirar el siguiente código, que define dos subprocesos con diferentes prioridades:
Hilo hilo2 = nuevo hilo ("alto")
{
ejecución pública vacía()
{
para (int i = 0; i < 5; i++)
{
System.out.println("El subproceso 2 se está ejecutando.");
}
}
};
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
hilo1.start();
hilo2.start();
}
hilo1.start();
}
Cómo crear un hilo
Todo el contenido anterior demuestra cierta información en el hilo predeterminado, entonces, ¿cómo crear un hilo? En Java, tenemos 3 formas de crear hilos.
Los subprocesos en Java heredan la clase Thread o implementan la interfaz Runnable. Repasémoslos uno por uno.
Utilice clases internas para crear hilos.
Podemos usar clases internas para crear subprocesos. El proceso consiste en declarar una variable de tipo Thread y anular el método de ejecución. El código de muestra es el siguiente:
Podemos derivar una clase de Thread y anular su método de ejecución de forma similar a lo anterior. El código de muestra es el siguiente:
vacío estático público createThreadBySubClass()
{
Hilo MiHilo = nuevo MiHilo();
hilo.start();
}
Podemos definir una clase para implementar la interfaz Runnable y luego usar una instancia de esta clase como parámetro para construir el constructor de la variable Thread. El código de muestra es el siguiente:
vacío estático público createThreadByRunnable()
{
MyRunnable ejecutable = new MyRunnable();
Hilo hilo = nuevo hilo (ejecutable);
hilo.start();
}
Esto implica el modo de ejecución de subprocesos múltiples en Java. Para Java, cuando se ejecutan subprocesos múltiples, existen diferencias entre "subprocesos múltiples de múltiples objetos" y "subprocesos múltiples de un solo objeto":
Multiobjeto, subprocesos múltiples , el programa crea múltiples objetos de subproceso durante la ejecución y se ejecuta un subproceso en cada objeto.
Subprocesos múltiples de un solo objeto , el programa crea un objeto de subproceso durante la ejecución y ejecuta varios subprocesos en él.
Obviamente, desde una perspectiva de programación y sincronización de subprocesos, el subproceso múltiple de múltiples objetos es más simple. De los tres métodos de creación de subprocesos anteriores, los dos primeros son "multiproceso multiobjeto" y el tercero puede utilizar "multiproceso multiobjeto" o "subproceso único de un solo objeto".
Veamos el siguiente código de muestra, que utilizará el método Object.notify. Este método activará un subproceso en el objeto y el método Object.notifyAll activará todos los subprocesos del objeto.
public static void main (String [] args) lanza InterruptedException
{
notificarPrueba();
notificarPrueba2();
notificarPrueba3();
}
NotifyTest de vacío estático privado () lanza InterruptedException
{
MiHilo[] arrThreads = nuevo MiHilo[3];
para (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = nuevo MiHilo();
arrThreads[i].id = i;
arrThreads[i].setDaemon(verdadero);
arrThreads[i].start();
}
Hilo.dormir(500);
para (int i = 0; i < arrThreads.length; i++)
{
sincronizado(arrThreads[i])
{
arrThreads[i].notificar();
}
}
}
NotifyTest2() vacío estático privado lanza InterruptedException
{
MiRunner[] arrMyRunners = new MiRunner[3];
Hilo[] arrThreads = nuevo hilo[3];
para (int i = 0; i < arrThreads.length; i++)
{
arrMisRunners[i] = nuevo MiRunner();
arrMyRunners[i].id = i;
arrThreads[i] = nuevo hilo(arrMyRunners[i]);
arrThreads[i].setDaemon(verdadero);
arrThreads[i].start();
}
Hilo.dormir(500);
para (int i = 0; i < arrMyRunners.length; i++)
{
sincronizado(arrMyRunners[i])
{
arrMyRunners[i].notify();
}
}
}
NotifyTest3() vacío estático privado lanza InterruptedException
{
Corredor MiRunner = nuevo MiRunner();
Hilo[] arrThreads = nuevo hilo[3];
para (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = nuevo hilo(corredor);
arrThreads[i].setDaemon(verdadero);
arrThreads[i].start();
}
Hilo.dormir(500);
sincronizado (corredor)
{
corredor.notifyAll();
}
}
}
clase MyThread extiende Thread
{
identificación interna pública = 0;
ejecución pública vacía()
{
System.out.println("El hilo " + id + " está listo para dormir durante 5 minutos.");
intentar
{
sincronizado (esto)
{
this.esperar(5*60*1000);
}
}
captura (Excepción interrumpida ex)
{
ex.printStackTrace();
}
System.out.println("El hilo "th" + id + "se despertó.");
}
}
clase MyRunner implementa Runnable
{
identificación interna pública = 0;
ejecución pública vacía()
{
System.out.println("El hilo " + id + " está listo para dormir durante 5 minutos.");
intentar
{
sincronizado (esto)
{
this.esperar(5*60*1000);
}
}
captura (Excepción interrumpida ex)
{
ex.printStackTrace();
}
System.out.println("El hilo "th" + id + "se despertó.");
}
}
El método notifyAll es adecuado para escenarios de "subprocesos múltiples de un solo objeto", porque el método de notificación solo activará aleatoriamente un subproceso en el objeto.
Cambio de estado de hilo
Para un hilo, desde el momento en que lo creamos hasta que finaliza, el estado del hilo durante este proceso puede ser así:
Creación: ya existe una instancia de Thread, pero la CPU todavía tiene recursos y intervalos de tiempo asignados.
Listo: el subproceso ha obtenido todos los recursos necesarios para ejecutarse y solo está esperando que la CPU programe el tiempo.
En ejecución: el subproceso está ubicado en el intervalo de tiempo actual de la CPU y está ejecutando la lógica relacionada.
Suspender: generalmente el estado después de llamar a Thread.sleep. En este momento, el subproceso todavía contiene varios recursos necesarios para la operación, pero la CPU no los programará.
Suspender: generalmente el estado después de llamar a Thread.suspend. Similar al modo de suspensión, la CPU no programará el subproceso. La diferencia es que en este estado, el subproceso liberará todos los recursos.
Muerte: el hilo finaliza o se llama al método Thread.stop.
A continuación demostraremos cómo cambiar los estados del hilo. Primero usaremos el siguiente método:
Thread() o Thread(Runnable): construye un hilo.
Thread.start: inicia un hilo.
Thread.sleep: cambia el hilo al estado de suspensión.
Thread.interrupt: Interrumpe la ejecución del hilo.
Thread.join: espera a que finalice un hilo.
Thread.yield: priva al hilo de su intervalo de tiempo de ejecución en la CPU y espera la siguiente programación.
Object.wait: bloquea todos los subprocesos del objeto y no continuará ejecutándose hasta que se active el método de notificación.
Object.notify: activa aleatoriamente un hilo en el objeto.
Object.notifyAll: activa todos los hilos en Object.
¡Ahora es el momento de la demostración! ! !
Hilo esperando y despertando
Los métodos Object.wait y Object.notify se utilizan principalmente aquí; consulte la instancia de notificación anterior. Cabe señalar que tanto esperar como notificar deben apuntar al mismo objeto. Cuando creamos un hilo implementando la interfaz Runnable, debemos usar estos dos métodos en el objeto Runnable en lugar del objeto Thread.
Hilo durmiendo y despertando
public static void main (String [] args) lanza InterruptedException
{
prueba de sueño();
}
SleepTest vacío estático privado () lanza InterruptedException
{
Hilo hilo = nuevo hilo()
{
ejecución pública vacía()
{
System.out.println("Thread" + Thread.currentThread().getName() + "Dormirá durante 5 minutos.");
intentar
{
Hilo.dormir(5*60*1000);
}
captura (Excepción interrumpida ex)
{
System.out.println("Thread" + Thread.currentThread().getName() + "El sueño fue interrumpido.");
}
System.out.println("Thread" + Thread.currentThread().getName() + "Sueño terminado.");
}
};
thread.setDaemon (verdadero);
hilo.start();
Hilo.dormir(500);
hilo.interrupt();
}
}
Aunque existe un método Thread.stop, este método no se recomienda. Podemos usar el mecanismo de suspensión y activación anterior para permitir que el subproceso finalice el subproceso al procesar IterruptedException.
public static void main (String [] args) lanza InterruptedException
{
detenerPrueba();
}
stopTest vacío estático privado () lanza InterruptedException
{
Hilo hilo = nuevo hilo()
{
ejecución pública vacía()
{
System.out.println("El hilo se está ejecutando.");
intentar
{
Hilo.dormir(1*60*1000);
}
captura (Excepción interrumpida ex)
{
System.out.println("Interrupción del hilo, fin del hilo");
devolver;
}
System.out.println("El hilo finalizó normalmente.");
}
};
hilo.start();
Hilo.dormir(500);
hilo.interrupt();
}
}
Cuando creamos 10 subprocesos en el subproceso principal, y luego esperamos que después de que se completen los 10 subprocesos, el subproceso principal ejecutará la siguiente lógica. En este momento, es hora de que aparezca Thread.join.
public static void main (String [] args) lanza InterruptedException
{
unirsePrueba();
}
joinTest vacío estático privado () lanza InterruptedException
{
Hilo hilo = nuevo hilo()
{
ejecución pública vacía()
{
intentar
{
para(int i = 0; i < 5; i++)
{
System.out.println("El hilo se está ejecutando.");
Hilo.dormir(1000);
}
}
captura (Excepción interrumpida ex)
{
ex.printStackTrace();
}
}
};
thread.setDaemon (verdadero);
hilo.start();
Hilo.dormir(1000);
hilo.join();
System.out.println("El hilo principal finalizó normalmente.");
}
}
Sabemos que todos los subprocesos de un proceso comparten espacio de memoria, entonces, ¿cómo transferimos mensajes entre diferentes subprocesos? Al revisar Java I/O, hablamos de PipedStream y PipedReader, y aquí es donde entran en juego.
Los dos ejemplos siguientes tienen exactamente las mismas funciones, la diferencia es que uno usa Stream y el otro usa Reader/Writer.
Hilo hilo1 = nuevo hilo()
{
ejecución pública vacía()
{
BufferedReader br = nuevo BufferedReader (nuevo InputStreamReader (System.in));
intentar
{
mientras (verdadero)
{
Mensaje de cadena = br.readLine();
pos.write(message.getBytes());
si (message.equals("end")) descanso;
}
br.cerrar();
pos.cerrar();
}
captura (Excepción ex)
{
ex.printStackTrace();
}
}
};
Hilo hilo2 = nuevo hilo()
{
ejecución pública vacía()
{
byte[] buffer = nuevo byte[1024];
int bytesLeer = 0;
intentar
{
while((bytesRead = pis.read(buffer, 0, buffer.length)) != -1)
{
System.out.println(new String(búfer));
if (new String(buffer).equals("end")) descanso;
búfer = nulo;
buffer = nuevo byte[1024];
}
pis.cerrar();
búfer = nulo;
}
captura (Excepción ex)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(verdadero);
thread2.setDaemon(verdadero);
hilo1.start();
hilo2.start();
hilo1.join();
hilo2.join();
}
Hilo hilo1 = nuevo hilo()
{
ejecución pública vacía()
{
BufferedReader br = nuevo BufferedReader (nuevo InputStreamReader (System.in));
intentar
{
mientras (verdadero)
{
Mensaje de cadena = br.readLine();
bw.write(mensaje);
bw.newLine();
bw.flush();
si (message.equals("end")) descanso;
}
br.cerrar();
pw.cerrar();
bw.cerrar();
}
captura (Excepción ex)
{
ex.printStackTrace();
}
}
};
Hilo hilo2 = nuevo hilo()
{
ejecución pública vacía()
{
Línea de cadena = nula;
intentar
{
mientras ((línea = br.readLine())! = nulo)
{
System.out.println(línea);
if (line.equals("end")) descanso;
}
br.cerrar();
pr.cerrar();
}
captura (Excepción ex)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(verdadero);
thread2.setDaemon(verdadero);
hilo1.start();
hilo2.start();
hilo1.join();
hilo2.join();
}