miércoles, 27 de marzo de 2013

Ejecución de Threads o hilos.

Buenas amigos, en este nuevo tema vamos a ver los hilos. Por norma general, y como ya he mencionado alguna vez, la JVM lee el código de arriba a abajo, ¿esto que quiere decir?, pues sencillamente que hasta que no termina de leer una linea no va a pasar a la siguiente, con lo cual no puede pasar a realizar un proceso mientras este ejecutando otro. Pero, ¿y si necesitamos que nuestra aplicación realice 2 o más procesos? Para ello utilizaremos los hilos, para que realicen procesos paralelos unos de otros, OJO, no los realiza simultáneamente si no que, con esto, no es necesario que la JVM termine de leer una linea de código, puede realizar otra proceso mientras acaba otro.

Bien, podemos crear hilos mediante:

  • Heredando la clase Thread
  • Implementando la interfaz Runnable.
Ambas nos permiten crear un hilo si bien, a mi personalmente, me gusta más implementar la interfaz Runnable ya que, de este modo, se nos permite heredar de otra clase que nos realice otras funciones. Aún así, voy a explicar ambos métodos para realizar hilos.

Clase Thread:

Cuando heredamos la clase Thread para crear un hilo, debemos sobreescribir el método run(), si o si. Este método nos permite ejecutar en un hilo todo el código que le pasemos.


Pero no penséis que el métod run() es el único que hay, a continuación veremos algunos métodos que controlan un hilo, supongamos que creamos una instancia de la clase anterior (Hilos h=new Hilos()):
  • void start(): Método que proporciona el estado "preparado" al hilo.
    • h.start();
  • static sleep(long milisegundos): Método que proporciona el estado "durmiendo" al hilo durante el tiempo pasado por argumentos medido en milisegundos, con lo cual 1000 milisegundos == 1 segundo.
    • Thread.sleep(1000);
    • Fijaos que al ser un método static no utilizo el objeto que hereda de Thread, si no que pongo el método con solo hacer referencia a la clase al que pertenece.
  • void getName(): Método para obtener el nombre del hilo.
    • h.getName();
  • void setName(String s): Método que establece un nombre para el hilo.
    • h.setName("picarcodigo");
  • static Thread currentThread(): Método que obtiene una referencia del hilo en ejecución.
    • Thread.currentThread();
  • void setPriority(int p): Método que establece un nivel de prioridad para el hilo. (1 mínimo - 10 máximo).
    • h.setPriority(10);
  • static void yield(): Método que devuelve un hilo en ejecución al estado "preparado". Esto permite a la JVM rotar recursos entre hilos de misma prioridad.
    • Thread.yield();
  • void join(): Método que une un hilo con el final de otro de modo que el primer hilo quedará en estado "preparado" hasta que el enlazado finalice.
    • h.join();
Aparte, tenemos otros métodos que la clase Thread hereda de Object que nos vienen muy bien:
  • void wait(): Método que hace que el hilo pase a estado "esperando".
    • h.wait();
  • void notify(): Método que "despierta" un hilo
    • h.notify();
  • void notifyAll(): Método que "despierta" todos los hilos.
    • h.notifyAll();
Bien, hay más, pero estos son los más usados.

Hemos ido viendo que hablábamos de estados cuando definíamos los métodos. Bien, debéis saber que un hilo puede estar en diferentes estados, los cuales podemos ver en las siguientes lineas:
  • Nuevo: Estado ocurrido cuando creamos la instancia de la clase que hereda de Thread. Permanece en este estado hasta que se ejecuta el método start().
  • Preparado: Este estado es producido cuando se llama al método start().
  • Ejecución: Este estado es producido cuando el hilo es seleccionado para su ejecución.
  • Esperando/bloqueado/dormido: Cualquiera de estos 3 estados es producido cuando se deja el estado preparado, y el hilo pasa a estar bloqueado, dormido o esperando algún tipo de proceso.
  • Finalizado: Este estado es producido cuando termina de ejecutar el método run(). Una vez finalizado no se puede volver al estado "preparado".
Bien, voy a crear un ejemplo en el que escribiré por consola un número por segundo:


Bien, por pasos:
  1. Instancio la clase en el método "main" pasando al "Estado Nuevo".
  2. Invoco el método start() pasando al "Estado Preparado".
  3. En el método run() creo un  bucle para que me imprima por pantalla los 10 primeros números, pasando el hilo al "Estado Ejecución" cuando me muestre el número por pantalla.
  4. Dormimos el hilo durante 1 seg con el método sleep() pasando a "Estado Dormido". Lo capturamos en un bloque try/catch.
  5. Una vez se termine el bucle for, el método run() dejará de ejecutarse, con lo que pasará a "Estado Finalizado".
Como deberíais observar os pinta un número por segundo hasta que llega a 10 y finaliza. Veamos como hacerlo con la interfaz Runnable.

Interfaz Runnable:

Esta interfaz nos permite ejecutar hilos como la clase Thread, y debemos de sobreescribir el método run() tal y como hicimos con la clase.


Como la clase anterior, deberemos meter el código en el método run() para que nos lo ejecute en paralelo. Esta interfaz no posee método de control del hilo por lo que deberemos de seguir necesitando la clase Thread para utilizar los suyos. Para ello utilizaremos uno de los constructores de la clase Thread:
  • Thread t=new Thread(Runnable obj);
A este constructor le pasaremos el objeto de la clase que implementa Runnable con lo cual podremos manejarlo con los métodos que nos proporciona la clase Thread

Voy a representar el ejemplo anterior con la interfaz Runnable.

Los mismos pasos que la anterior clase salvo una variable:
  1. Instancio la clase que implementa Runnable.
  2. Instancio la clase Thread y le paso el objeto de la clase anterior por argumentos pasando al "Estado Nuevo".
  3. Invoco el método start() pasando al "Estado Preparado".
  4. En el método run() creo un  bucle para que me imprima por pantalla los 10 primeros números, pasando el hilo al "Estado Ejecución" cuando me muestre el número por pantalla.
  5. Dormimos el hilo durante 1 seg con el método sleep() pasando a "Estado Dormido". Lo capturamos en un bloque try/catch.
  6. Una vez se termine el bucle for, el método run() dejará de ejecutarse, con lo que pasará a "Estado Finalizado".
Bueno, hay otra forma que podéis ver por la red, que trata de crear una instancia de Thread y pasarle una clase anónima de Runnable. No voy a entretenerme en explicar ahora lo que es una clase anónima, en temas posteriores lo veremos, solo con saber :

  • Es una clase Interna.
  • No tiene nombre.
  • Es definida en una linea de código, por lo que debe acabar con punto y coma " ; ".
  • Puede ser usada como argumento de un método.
El código de un hilo con clase anónima sería así:


He realizado el mismo ejemplo que las versiones anteriores. Como podéis observar, ya no heredo o implemento nada, si no que en la main creo una instancia de de la clase Thread a la cual le paso por el constructor una clase anónima de Runnable, en vez de un objeto de una clase que la implemente.
La creación explicada paso a paso sería:
  • Creo una instancia de la clase Thread y le paso la clase anónima:
    • Thread t1=new Thread(new Runnable(){
    • });
      • Como veis en la clase Runnable abro llaves para introducir el código de la clase y cierro el paréntesis de la linea de argumentos y agrego el punto y coma " ; ". No olvidemos que estaos creando una clase en una linea de argumentos.
  • Sobrescribo el método run().
    • Thread t1=new Thread(new Runnable(){
    • public void run(){
    • }
    • });
  • Escribimos dentro del run() lo que queremos que nos ejecute en paralelo.
    • Thread t1=new Thread(new Runnable(){
    • public void run(){
    • for(i=0;i<10;i++){
    • System.out.println(i+1);
    • try{
    • Thread.sleep(1000);
    • }catch(InterruptedException e){
    • e.printStackTrace();
    • }
    • }
    • }
    • });
  • Invocamos el método start().
    • Thread t1=new Thread(new Runnable(){
    • public void run(){
    • for(i=0;i<10;i++){
    • System.out.println(i+1);
    • try{
    • Thread.sleep(1000);
    • }catch(InterruptedException e){
    • e.printStackTrace();
    • }
    • }
    • }
    • });t1.start();
Bueno amigos, por mi parte es todo, espero haberme expresado con claridad y no os surjan demasiadas dudas, que lo harán. Es posible que me deje cosas sin mencionar, como los hilos sincronizados. No os preocupéis, en el próximo tema lo veremos. 

Un saludo

1 comentario:

  1. Pon el código completo para compilarlo y probarlo por favor

    ResponderEliminar