En esta segunda entrega de hilos vamos a trabajar un poco mas con ellos teniendo en cuenta los conceptos que vimos en la primera entrega.

Trabajando con Hilos

Conozcamos un poco el API de Thread para ver que podemos hacer con ella.

Podemos obtener una referencia al hilo actual: Obteniendo la referencia del hilo actual podemos manipularlo según lo que necesitemos.

1
Thread hiloActual = Thread.currentThread();


Revisando un poco el API de Thread podemos encontrar algunos métodos interesantes:

Conocer el id y el nombre de un hilo.

1
2
3
Thread hiloActual = Thread.currentThread();
long id = hiloActual.getId();
String nombre hiloActual.getName();


Saber la prioridad.

La clase Thread tiene 3 constantes enteras(int) que especifican los limites y el punto de medio para la prioridad de un hilo y que según la implementación de la JVM podría ser diferentes.

  • Prioridad mínima: Thread.MIN_PRIORITY
  • Prioridad máxima: Thread.MAX_PRIORITY
  • Prioridad normal: Thread.NORM_PRIORITY

Trabajamos la prioridad con su get y set correspondiente, getPriority() y setPriority(int).

Cediendo el paso

Es posible que según el caso tomemos la decisión de que el hilo que se esta ejecutando actualmente le ceda el paso a otro de la piscina de hilos. Pero no le decimos al planificador cual hilo seleccionar para ejecutarse, solo le decimos “oye me voy a la piscina envía a otro a ejecutarse”, pero es posible que incluso decida que el hilo actual que cedió el paso regrese a ejecutarse. Si recordamos el ciclo de vida de un hilo vemos que se puede ir de ejecutandose a ejecutable y esto se consigue cediendo el paso con el método static yield().

1
2
3
Thread hiloActual = Thread.currentThread();
Thread.yield(); // Metodo static
hiloActual.yield(); // produce el mismo efecto


Hilos dormilones

Podemos poner a dormir un hilo con el método static sleep(), cuando este despierta regresa a ejecutable de ahí el planificador decidirá cuando enviarlo a ejecutar de nuevo. Cual es la utilidad de esto?, bueno podemos tener diferentes razones importantes una podría ser que la operación que realiza consume mucho ancho de banda por ejemplo. Es cuestión de criterios en nuestro diseño.

1
2
3
4
5
6
7
8
Thread hiloActual = Thread.currentThread();
try {
  // 5 Segundos (5000 milis)
  Thread.sleep(5000); // Metodo static
  hiloActual.sleep(5000); // produce el mismo efecto
} catch (InterruptedException ex) {
  ex.printStackTrace();
}


Note que debemos usar un try ya que es posible que se lanze una excepcion si este hilo es interrumpido de su estado actual.

Encadenando hilos

Es posible que encadenemos un hilo a otro, es decir que un hilo no empieza a ejecutarse hasta que otro termine. Que su estado sea terminado o muerto. El método no estático join() permite hacer esto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class HiloUno extends Thread{
    private String s;

    public HiloUno(String name) {
        this.s = name;
    }

    @Override
    public void run() {
        for(int x = 0; x <4; x++){
            System.out.println(this.s);
        }
    }
}

public class Main {

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

        HiloUno t1 = new HiloUno("A");
        t1.start();
        t1.join(); // Encadenamos el actual a t1
        System.out.println("FIN MAIN");

    }
}


En este ejemplo estamos encadenando el hilo actual(main en este caso) a t1, la impresión de “FIN MAIN” solo aparecerá hasta que se imprima cuatro veces la letra “A” que pasamos en el constructor de t1.

Ahora como encadenamos dos hilos diferentes que no sea el actual main?. Recordemos que join() toma el hilo actual que se esta ejecutando y lo encadena a otro. Si queremos encadenar t2 a t1 podriamos hacerlo al iniciar t2 teniendo una referencia de t1.

Veamos el código.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class HiloUno extends Thread{

    private String s;
    private Thread padre;

    public HiloUno(String name) {
        this.s = name;
    }

    public void setPadre(Thread padre) throws InterruptedException{
        this.padre = padre;
    }

    @Override
    public void run(){
        try {
            if(padre != null) {
                padre.join(); // Encadenamos
            }
            for(int x = 0; x <4; x++){
                System.out.println(this.s);
                Thread.sleep(2000); // Que duerma 2 segundos
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

public class Main {

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

        HiloUno t1 = new HiloUno("A");
        HiloUno t2 = new HiloUno("B");

        t2.setPadre(t1);
        t1.start();
        t2.start();

        System.out.println("FIN MAIN");

    }
}


Bueno ahí lo que hicimos fue ver en run() si t2 tiene un padre para encadenarse. Así t2 debe esperar a que su padre(t1) termine. Usamos sleep para ayudarnos a ver el proceso lentamente cuando ejecutemos este programa.

Hilos en Java - Parte 1


Franky Villadiego

Volando hacia el desarrollo productivo!