Propiedades de los hilos en Java
Nombres de hilos
Al crear un hilo en Java podemos darle un nombre. El nombre nos puede ayudar a diferenciarlo entre los diferentes hilos que se están ejecutando. Por ejemplo, si varios hilos escriben con system.out
, puede ser útil saber qué hilo escribió el hilo. Un ejemplo:
1Thread thread = new Thread("Hilo Nuevo") {2 public void run() {3 System.out.println("Ejecutando hilo con nombre " + getName());4 }5};6thread.start();
Acá vemos cómo pasamos la cadena "Hilo Nuevo"
como argumento al constructor de Thread
. Esta cadena es el nombre del hilo. El nombre se puede obtener usando el método getName()
de la clase Thread
. También podemos pasar un nombre a un Thread
usando una implementación de Runnable
. Un ejemplo:
1Runnable runnable = new MyRunnable();2Thread thread = new Thread(runnable, "Hilo Nuevo");3
4thread.start();5System.out.println("Hilo de nombre: " + thread.getName());
Notamos acá que, como la clase MyRunnable
no es una subclase de Thread
, no tiene acceso al método getName()
. Sin embargo, el hilo creado con el nombre "Hilo Nuevo"
sí tiene acceso a este método.
Método Thread.currentThread()
El méþodo Thread.currentThread()
devuelve una referencia a la instancia de Thread
que ejecutando currentThread()
. Así podemos obtener acceso al objeto Thread
que represente al hilo que se ejecuta en el bloque de código dado. Aquí hay un ejemplo:
1Thread thread = Thread.currentThread();
Una vez que referenciamos un objeto Thread
, podemos llamar sus métodos. Por ejemplo, podemos obtener el nombre del hilo actual:
1Thread thread = Thread.currentThread();2System.out.println("Hilo actual: " + thread.getName());
Ejemplo de uso hilos en Java
Vamos a ver un ejemplo de uso de hilos. En el ejemplo, primero se imprime el nombre del hilo que ejecuta el método main()
. Este hilo es asignado por la JVM. Luego se inician 10 hilos y se les da un número como nombre (""+ i)
. Cada hilo, luego imprime su nombre, y deja de ejecutarse.
1public class Main {2 public static void main(String[] args) {3 Thread mainThread = Thread.currentThread();4 System.out.println("Hilo principal: " + mainThread.getName());5
6 for (int i = 0; i < 10; i++) {7 Thread thread = new Thread("" + i) {8 public void run() {9 System.out.println("Ejecutando hilo con nombre " + getName());10 }11 };12 thread.start();13 }14 }15}
Observemos bien, que, incluso si los hilos se inician secuencialmente (1, 2, 3, etc), tal vez no se ejecuten en ese orden. La JVM decide cuándo y en qué orden se ejecutan los hilos. Por lo tanto, el orden en que se imprimen los nombres de los hilos puede variar.
Pausar un hilo
Un hilo se puede pausar a sí mismo llamando al método estático Thread.sleep()
. El método sleep()
toma un argumento entero que representa la cantidad de milisegundos que el hilo debe dormir. El método intentará dormir ese número de milisegundos antes de reanudar la ejecución. El méþodo no es 100% preciso, pero aún así es útil. Aquí hay un ejemplo:
1Thread thread = new Thread() {2 public void run() {3 System.out.println("Hilo durmiendo");4 try {5 Thread.sleep(1000L);6 } catch (InterruptedException e) {7 e.printStackTrace();8 }9 System.out.println("Hilo finalizado");10 }11};12thread.start();
En este ejemplo, el hilo se duerme por 1000 milisegundos (1 segundo) antes de imprimir “Hilo finalizado”.
Detener un hilo
Detener un hilo en Java necesita algo de preparación en la implementación del código. La clase Thread
contiene un método stop()
, pero está deprecado. Este método no brinda ninguna garantía de que el hilo se detenga de manera segura. Eso significa que todos los objetos Java a los que tuvo acceso ese hilo durante su ejecución quedarán en un estado desconocido. Si otros hilos en la aplicación también tienen acceso a los mismos objetos, nuestra aplicación puede fallar de manera impredecible.
En lugar de llamar al método stop()
, tendremos que implementar el código para detenerlo. Este sería un ejemplo de una clase que implementa Runnable
con un método stopThread()
que le da la señal para que se detenga. El hilo comprobará esta señal y se detendrá cuando esté listo.
1public class MyRunnable implements Runnable {2
3 private boolean stop = false;4
5 public synchronized void stopThread() {6 this.stop = true;7 }8
9 private synchronized boolean keepRunning() {10 return this.stop == false;11 }12
13 @Override14 public void run() {15 while (keepRunning()) {16 // Seguir ejecutando lo que sea que haga en el hilo17 System.out.println("Hilo en ejecución");18 try {19 Thread.sleep(2000);20 } catch (InterruptedException e) {21 e.printStackTrace();22 }23 }24 }25}
Observemos los métodos stopThread()
y keepRunning()
. El método stopThread()
está pensado para ser llamado desde otro hilo diferente al que ejecuta el método run()
de la clase MyRunnable
. El método keepRunning()
es llamado internamente por el hilo que ejecuta run()
. Siempre que no se llame a stopThread()
, el método keepRunning()
devolverá true
, y el hilo seguirá ejecutándose. Cuando se llama a stopThread()
, el método keepRunning()
devolverá false
, y el hilo se detendrá.
Aquí tenemos un ejemplo de iniciio de un hilo que ejecuta una instancia de MyRunnable
, y la detiene luego de un tiempo:
1MyRunnable myRunnable = new MyRunnable();2Thread thread = new Thread(myRunnable);3thread.start();4
5try {6 Thread.sleep(10_000);7} catch (InterruptedException e) {8 e.printStackTrace();9}10
11myRunnable.stopThread();
Este ejemplo primero crea una instancia de MyRunnable
, luego pasa esa instancia a un hilo y lo inicia. Luego, el hilo principal se duerme por 10 segundos. Durante este tiempo, el hilo creado con MyRunnable
se ejecuta y se imprime “Hilo en ejecución” cada 2 segundos. Después de 10 segundos, el hilo principal llama a stopThread()
, y el hilo creado con MyRunnable
se detiene.
Método join()
de la clase Thread
El método join()
de la clase Thread
se puede usar para esperar a que un hilo termine su ejecución antes de que el hilo actual continúe. Aquí hay un ejemplo:
1MyRunnable myRunnable = new MyRunnable();2Thread thread = new Thread(myRunnable);3thread.start();4
5try {6 thread.join();7} catch (InterruptedException e) {8 e.printStackTrace();9}
En este ejemplo, el hilo principal espera a que el hilo creado con MyRunnable
termine su ejecución antes de continuar. Si el hilo creado con MyRunnable
ya ha terminado, el método join()
regresa inmediatamente. Si el hilo aún está en ejecución, el hilo principal se bloquea hasta que el hilo creado con MyRunnable
termine.