Logo Studenta

Tema_9_Hilos_con_la_interface_Runnable_Clase_10

¡Estudia con miles de materiales!

Vista previa del material en texto

MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE 
-1- Año 2016 
Implementación de Hilos usando la interface Runnable en 
JAVA 
 
Introducción 
En las últimas dos clases se analizó la implementación de Hilos usando herencia a 
través de la clase Thread, a continuación se realizará dicha tarea pero a través de la 
interfaz Runnable. 
Una interface solamente puede contener métodos abstractos y/o variables estáticas 
y finales (constantes). Las clases, por otro lado, pueden implementar métodos y contener 
variables que no sean constantes. Una interface no puede implementar cualquier método. 
Una clase que implemente una interface debe implementar todos los métodos definidos 
en esa interface. Una interface tiene la posibilidad de poder extenderse de otras interfaces 
y, al contrario que las clases, puede extenderse de múltiples interfaces. Además, una 
interface no puede ser instanciada con el operador new. 
Se debe destacar que la técnica más adecuada para la creación de aplicaciones 
multitarea en Java se basa en la implementación de la interfaz Runnable, en vez de la 
extensión de la clase Thread. 
Para ello se deberán realizar las siguientes acciones: 
o Implementar el método run() de la interfaz Runnable. 
o Creación y ejecución de tareas. 
La interfaz Runnable debe ser implementada por cualquier clase cuyas instanc ias 
sean ejecutadas por un hilo. Dicha clase debe implementar el método run(). 
La implementación de la interfaz Runnable es la forma más habitual de crear tareas, 
ya que proporciona al desarrollador una forma para agrupar el trabajo de infraestruc tura 
de la clase. La interfaz establece el trabajo a realizar y la clase o clases que la 
implementan, indican cómo realizar ese trabajo, se puede construir un hilo sobre cualquier 
objeto que implemente la interfaz Runnable. 
Para implementar esta interfaz, una clase solo tiene que implementar el método 
run(). Dentro del run() se define el código que constituye el nuevo hilo. Después de 
haberse creado una clase que implemente la interfaz Runnable se requiere crear un objeto 
del tipo Thread dentro de esa clase, usando cualquiera de los constructores de Thread. 
 
 
MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE 
-2- Año 2016 
// crear una clase con hilos 
public class MiClase implements Runnable { 
 Thread unHilo; 
 MiClase() { 
 unHilo = new Thread(); 
 } 
 public void run() { 
 if (unHilo != null) { 
 //cuerpo del hilo 
 } 
 } 
} 
La siguiente forma general crea el hilo e inicia su ejecución en la clase que usa a 
MiClase: 
MiClase miHilo = new MiClase (); 
new Thread (miHilo).start(); 
 
La siguiente es la forma general del cuerpo de un hilo mediante implementación de 
la interfaz Runnable: 
 
Implementación del método run() 
Como ya se dijo la interfaz Runnable incluye un único método run(), que como 
sucede con la clase Thread, será el que deba incluir las acciones a realizar por las tareas. 
Codificar este método mediante la implementación de la interfaz Runnable permite 
a la clase heredar al mismo tiempo la funcionalidad de alguna otra clase existente. 
A continuación, se presenta el código de una clase para la impresión de los nombres 
de los thread implementando la interfaz Runnable: 
 
public class TareaRb implements Runnable 
{ 
 public void run() 
 { 
 for (int i=1; i<=100; i++) 
 { 
 System.out.println("Nombre " + Thread.currentThread().getName()); 
 try { Thread.sleep(100); } 
 catch (InterruptedException e) { e.printStackTrace(); } 
 } 
 } 
} 
 
MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE 
-3- Año 2016 
Como se puede observar, el método run() no difiere en absoluto del de la clase 
anterior. Sin embargo, hay que destacar que al no heredar Thread, no dispone de un 
constructor al que pasarle el nombre de la tarea; lo que se debe analizar es porque el 
método getName() devuelve el nombre de la tarea en ejecución. La respuesta es que la 
llamada a currentThread() no devuelve un objeto TareaRb sino un objeto de la clase 
Thread que representa la tarea en ejecución. 
 
Creación y ejecución de tareas 
Como ya se ha mencionado toda tarea en ejecución multitarea es un thread. Esto 
significa, que habrá que crear tantos objetos de la clase Thread como tareas queramos 
poner en ejecución, el tema es que el código asociado a esas tareas debe ser el que está 
definido en el método run() de la clase que implementa a Runnable(). 
Para crear objetos Thread con estas características, se debe utilizar alguno de los 
siguientes constructores de la clase Thread: 
Thread(Runnable obj) 
Thread (Runnable obj, String nombre) 
 
En ambos casos, la creación y ejecución de las tareas en el ejemplo de la impresión 
de nombres quedaría de la siguiente forma: 
 
public class Principal 
{ 
 public static void main (String [] args) 
 { 
 // Se crea un único objeto tarearb que se comparte en cada thread 
 TareaRb t = new TareaRb(); 
 // Las tareas son instancias de la clase Thread 
 Thread t1 = new Thread(t,"pepe"); 
 Thread t2 = new Thread(t,"ana"); 
 Thread t3 = new Thread(t,"juan"); 
 // Los Threads se ponen en ejecución 
 t1.start(); 
 t2.start(); 
 t3.start(); 
 } 
} 
 
 
 
MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE 
-4- Año 2016 
Ejemplos sin implementación de hilos, implementación con 
Thread e implementación con interface Runnable 
Si se simula el proceso de cobro de un supermercado; donde los clientes adquieren 
productos y una cajera les cobra los productos, pasándolos uno a uno por el escaner de la 
caja registradora. En este caso la cajera debe de procesar la compra cliente a cliente, es 
decir que primero le cobra al cliente 1, luego al cliente 2 y así sucesivamente. Para ello 
vamos a definir una clase “Cajera” y una clase “Cliente” el cual tendrá un “array de 
enteros” que representaran los productos que ha comprado y el tiempo que la cajera 
tardará en pasar el producto por el escáner; es decir, que si tenemos un array con [1,3,5] 
significará que el cliente ha comprado 3 productos y que la cajera tardara en procesar el 
producto 1 ‘1 segundo’, el producto 2 ‘3 segundos’ y el producto 3 en ‘5 segundos’, con 
lo cual tardara en cobrar al cliente toda su compra ‘9 segundos’. 
Clase “Cajera.java“: 
public class Cajera { 
 private String nombre; 
 public Cajera(String nombre) { 
 super(); 
 this.nombre = nombre; 
 } 
 public void procesarCompra(Cliente cliente, long timeStamp) { 
 System.out.println("La cajera " + this.nombre 
 + " COMIENZA A PROCESAR LA COMPRA DEL CLIENTE " 
 + cliente.getNombre() + " EN EL TIEMPO: " 
 + (System.currentTimeMillis() - timeStamp) / 1000 + "seg"); 
 for (int i = 0; i < cliente.getCarroCompra().length; i++) { 
 this.esperarXsegundos(cliente.getCarroCompra()[i]); 
 System.out.println("Procesado el producto " + (i + 1) 
 + " ->Tiempo: " + (System.currentTimeMillis() - timeStamp) 
 / 1000 + "seg"); 
 } 
 System.out.println("La cajera " + this.nombre 
 + " HA TERMINADO DE PROCESAR " + cliente.getNombre() 
 + " EN EL TIEMPO: " + (System.currentTimeMillis() - timeStamp) 
 / 1000 + "seg"); 
 } 
 private void esperarXsegundos(int segundos) { 
 try { 
 Thread.sleep(segundos * 1000); 
 } catch (InterruptedException ex) { 
 Thread.currentThread().interrupt(); 
 } 
 } } 
 
MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE-5- Año 2016 
Clase “Cliente.java“: 
 
public class Cliente { 
 private String nombre; 
 private int[] carroCompra; 
 public Cliente(String nombre, int[] carroCompra) { 
 super(); 
 this.nombre = nombre; 
 this.carroCompra = carroCompra; 
 } 
 
 public String getNombre() { 
 return nombre; 
 } 
 
 public void setNombre(String nombre) { 
 this.nombre = nombre; 
 } 
 public int[] getCarroCompra() { 
 return carroCompra; 
 } 
 public void setCarroCompra(int[] carroCompra) { 
 this.carroCompra = carroCompra; 
 } 
} 
 
 
MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE 
-6- Año 2016 
Si se ejecuta este programa propuesto con dos Clientes y con un solo proceso (que 
es lo que se suele hacer normalmente), se procesaría primero la compra del Cliente 1 y 
después la del Cliente 2, con lo cual se tardará el tiempo del Cliente 1 + Cliente 2. A 
continuación se verá como se programa el método Main para lanzar el programa. 
CUIDADO: Aunque hayamos puesto dos objetos de la clase Cajera (cajera1 y cajera2) 
no significa que tengamos dos cajeras independientes, lo que estamos diciendo es que 
dentro del mismo hilo se ejecute primero los métodos de la cajera1 y después los métodos 
de la cajera2, por tanto a nivel de procesamiento es como si tuviésemos una sola cajera: 
 
Clase “Main.java“: 
 
public class Main { 
 public static void main(String[] args) { 
 Cliente cliente1 = new Cliente("Cliente 1", new int[] { 2, 2, 1, 5, 2, 3 }); 
 Cliente cliente2 = new Cliente("Cliente 2", new int[] { 1, 3, 5, 1, 1 }); 
 Cajera cajera1 = new Cajera("Cajera 1"); 
 Cajera cajera2 = new Cajera("Cajera 2"); 
 // Tiempo inicial de referencia 
 long initialTime = System.currentTimeMillis(); 
 cajera1.procesarCompra(cliente1, initialTime); 
 cajera2.procesarCompra(cliente2, initialTime); 
 } 
} 
 
Si se ejecuta este ejemplo se observa cómo se procesa primero la compra del cliente 
1 y después la compra del cliente 2 tardando en procesar ambas compras un tiempo de 26 
segundos. 
 
 
Como se ha visto se puede procesar los dos clientes a la vez si hubiese dos cajeras 
y se asignara uno a cada cliente. Para ello se debe modificar la clase “Cajera.java” y hacer 
que esta clase herede de la clase Thread para heredar y sobre-escribir algunos de sus 
métodos. Primero se verá como codificar esta nueva clase “CajeraThread.java” y después 
se explicará sus características. 
 
 
MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE 
-7- Año 2016 
public class CajeraThread extends Thread { 
 private String nombre; 
 private Cliente cliente; 
 private long initialTime; 
 
 // Constructor, getter & setter 
 public CajeraThread(String nombre, Cliente cliente, long initialTime) { 
 super(); 
 this.nombre = nombre; 
 this.cliente = cliente; 
 this.initialTime = initialTime; 
 } 
 @Override 
 public void run() { 
 System.out.println("La cajera " + this.nombre + " COMIENZA A PROCESAR 
LA COMPRA DEL CLIENTE " 
 + this.cliente.getNombre() + " EN EL TIEMPO: " 
 + (System.currentTimeMillis() - this.initialTime) / 
1000 
 + "seg"); 
 for (int i = 0; i < this.cliente.getCarroCompra().length; i++) { 
 this.esperarXsegundos(cliente.getCarroCompra()[i]); 
 System.out.println("Procesado el producto " + (i + 1) 
 + " del cliente " + this.cliente.getNombre() + "->Tiempo: " 
 + (System.currentTimeMillis() - this.initialTime) / 1000 
 + "seg"); 
 } 
 System.out.println("La cajera " + this.nombre + " HA TERMINADO DE 
PROCESAR " 
 + this.cliente.getNombre() + " EN EL 
TIEMPO: " 
 + (System.currentTimeMillis() - 
this.initialTime) / 1000 
 + "seg"); 
 } 
 private void esperarXsegundos(int segundos) { 
 try { 
 Thread.sleep(segundos * 1000); 
 } catch (InterruptedException ex) { 
 Thread.currentThread().interrupt(); 
 } 
 } 
} 
 
Lo primero que se ve es que la clase “CajeraThread” debe de heredar de la clase 
Thread: “extendsThread“. 
 
 
MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE 
-8- Año 2016 
Otra cosa importante que vemos es que hemos sobre-escrito el método “run()” (de 
ahí la etiqueta @Override) . Este método es imprescindibles sobre-escribirlo (ya que es 
un método que está en la clase Runnable y la clase Thread Implementa esa Interface) 
porque en él se va a codificar la funcionalidad que se ha de ejecutar en un hilo; es decir, 
que lo que se programe en el método “run()” se va a ejecutar de forma secuencial en un 
hilo. En esta clase “CajeraThread” se pueden sobre-escribir más métodos para que hagan 
acciones sobre el hilo o thread como por ejemplo, parar el thread, ponerlo en reposos, etc. 
A continuación se verá como programar el método Main para que procese a los clientes 
de forma paralela y se analizará como se tarda menos en procesar todo. El método Main 
está en la clase “MainThread.java” que tiene el siguiente contenido: 
 
public class MainThread { 
 public static void main(String[] args) { 
 Cliente cliente1 = new Cliente("Cliente 1", new int[] { 2, 2, 1, 5, 2, 3 }); 
 Cliente cliente2 = new Cliente("Cliente 2", new int[] { 1, 3, 5, 1, 1 }); 
 // Tiempo inicial de referencia 
 long initialTime = System.currentTimeMillis(); 
 CajeraThread cajera1 = new CajeraThread("Cajera 1", cliente1, 
initialTime); 
 CajeraThread cajera2 = new CajeraThread("Cajera 2", cliente2, 
initialTime); 
 cajera1.start(); 
 cajera2.start(); 
 } 
} 
 
Si se ejecuta se puede comprobar cómo efectivamente el programa se ejecuta de 
forma paralela y tarda solo 15 segundos en terminar su ejecución. 
En este ejemplo se observa el efecto de que las dos cajeras procesan la compra de 
los clientes de forma paralela sin que el resultado de la aplicación sufra ninguna variación 
en su resultado final, que es el de procesar todas las compras de los clientes de forma 
independiente. De forma gráfica vemos que el programa ha realizado lo siguiente en dos 
hilos distintos: 
 
 
MODELOS DE DESARROLLO DE PROGRAMAS Y PROGRAMACION CONCURRENTE 
-9- Año 2016 
Otra forma de hacer lo mismo pero sin heredar de la clase “Thread” es implementar 
la Interface “Runnable”. En este caso no se dispondrá ni se podrá sobre-escribir los 
métodos de la clase Thread ya que no se va a utilizar y solo se va a tener que sobre-escribir 
el método “run()“. En este caso solo será necesario implementar el método “run()” para 
que los procesos implementados en ese método se ejecuten en un hilo diferente. Ahora se 
verá un ejemplo de cómo utilizando objetos de las clases “Cliente.java” y “Cajera.java” 
se puede implementar la multitarea en la misma clase donde se llama al método Main de 
la aplicación, a continuación se muestra la codificación en la clase “MainRunnable.java“: 
public class MainRunnable implements Runnable{ 
 private Cliente cliente; 
 private Cajera cajera; 
 private long initialTime; 
 public MainRunnable (Cliente cliente, Cajera cajera, long initialTime){ 
 this.cajera = cajera; 
 this.cliente = cliente; 
 this.initialTime = initialTime; 
 } 
 public static void main(String[] args) { 
 Cliente cliente1 = new Cliente("Cliente 1", new int[] { 2, 2, 1, 5, 2, 3 }); 
 Cliente cliente2 = new Cliente("Cliente 2", new int[] { 1, 3, 5, 1, 1 }); 
 Cajera cajera1 = new Cajera("Cajera 1"); 
 Cajera cajera2 = new Cajera("Cajera 2"); 
 // Tiempo inicial de referencia 
 long initialTime = System.currentTimeMillis(); 
 Runnable proceso1 = new MainRunnable(cliente1, cajera1, initialTime);Runnable proceso2 = new MainRunnable(cliente2, cajera2, initialTime); 
 new Thread(proceso1).start(); 
 new Thread(proceso2).start(); } 
 @Override 
 public void run() { 
 this.cajera.procesarCompra(this.cliente, this.initialTime); 
 } } 
Basado en: 
 García de Jalón J, Rodríguez J, Mingo I, Imaz A, Brazalez A, Larzabal A, 
Calleja J y García J. 2.000. Aprenda Java como si estuviera en primero. 
 Páginas web consultadas, accedidas en Octubre de 2.014: 
o http://guniu.mx/progravanzada/unidad4.1.html 
o http://hopzone.com.es/KIMERAWEB/java/tema55.php 
o http://jarroba.com/multitarea-e-hilos-en-java-con-ejemplos-thread-
runnable/ 
o http://proton.ucting.udg.mx/tutorial/java/Cap7/creath.html 
 Sánchez Jorge. 2.004. Java2 incluye Swing, Threads, programación en red, 
Javabeans, JDBC y JSP / Servlets.

Continuar navegando

Materiales relacionados

15 pag.
Tema 15 Clase 15 - Runnable

UNAM

User badge image

campuzanobeni3

128 pag.
POOJava_cursoIndra

User badge image

zulmaibalo512

90 pag.
JavaDesdeCero

User badge image

vicky angel