Descarga la aplicación para disfrutar aún más
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.
Compartir