Práctica 11 San Sebastián, mayo 1999 Programación Java Javier García de Jalón · José Ignacio Rodríguez Alfonso Brazález · Alberto Larzabal · Jesús Calleja · Jon García Informática 2: Práctica nº 11 página 1 INDICE Ejercicio 1: Ejercicio 2: Ejercicio 3: Ejercicio 4: Ejercicio 5: Ejercicio 6: Crear una aplicación sencilla que arranque un thread............................................................... 1 Crear una aplicación que arranque dos threads ........................................................................ 2 Crear una aplicación que arranque tres threads ........................................................................ 2 Crear una aplicación gráfica con tres threads que simule una carrera ....................................... 2 Nodificar el ejercicio anterior para simular una carrera con prioridades ................................... 5 Crear la aplicación Reloj......................................................................................................... 5 Antes de comenzar la práctica abre el Windows Explorer y comprueba que se ha creado de modo automático en tu disco G:\ un directorio llamado Inf2prac11. No deberás moverlo a otro subdirectorio ni cambiarle de nombre. Por motivos de orden es importante que todos los ejercicios de esta práctica se creen dentro de este directorio, porque esta semana se recogerá la práctica. Como recomendación general, mantén abierto el Windows Explorer y comprueba de vez en cuando que los proyectos de los distintos ejercicios se están guardando correctamente. Hay que evitar copiar los ejercicios de otra persona, principalmente porque así no se aprende a programar: a programar sólo se aprende programando. Puedes utilizar también Windows Explorer para ayudar a Visual J++ 6.0 a crear un proyecto nuevo a partir de los ficheros del anterior. Ejercicio 1: Crear una aplicación sencilla que arranque un thread En este ejercicio se trata de crear una clase derivada de Thread llamada SimpleThread. El siguiente programa crea un thread con nombre “Jamaica”. El método run() de este thread imprime diez veces un mensaje con el número y el nombre del thread, esperando un tiempo aleatorio entre cero y tres segundos. El programa principal main() crea un thread y los arranca. Como el entorno Visual J++ cierra la consola y la aplicación en cuanto termina la ejecución, sin permitir ver con calma los resultados, se ha incluido una “espera” por medio de un mensaje y la lectura de un carácter, que debe producirse antes de terminar la aplicación. Crea un proyecto llamado Ejer1 que contenga el siguiente fichero: // fichero SimpleThread.java import java.io.*; public class SimpleThread extends Thread { // constructor public SimpleThread(String name) { super(name); } // método principal del thread public void run() { for (int i=0; i<10; i++) { System.out.println(i + " " + getName()); try { sleep((int)(Math.random()*3000)); } catch (InterruptedException e) {} } System.out.println("Done!" + getName()); } Informática 2: Práctica nº 11 página 2 // programa principal public static void main (String[] args) { // se crea un thread con el nombre "Jamaica" SimpleThread st1 = new SimpleThread("Jamaica"); // se arranca el thread st1.start(); // se espera a pulsar Intro para terminar try { while (currentThread().activeCount() > 1) { currentThread().sleep(100); } System.out.print("Pulse return para finalizar "); System.in.read(); } catch (IOException e) {} catch (InterruptedException ie) {} } } // fin de la clase SimpleThread En realidad este programa utiliza dos threads: uno creado explícitamente y otro el thread en el que corre el programa main(). Si no se indica nada y para esperar se pone simplemente el código try { System.out.print("Pulse return para finalizar "); System.in.read(); } catch (IOException e) {} el mensaje de “Pulse return…” se escribe antes que los diez mensajes del thread puesto que el thread de main() se puede ejecutar más rápidamente (la “espera” a que el usuario teclee un carácter siempre queda al final, pero no el mensaje citado). Para evitar esto se he introducido un bucle de espera basado en un while que pregunta el número de threads abiertas: mientras haya más de un thread activo se espera; cuando sólo queda el thread del programa principal se imprime el mensaje. Ejercicio 2: Crear una aplicación que arranque dos threads Crea un proyecto llamado Ejer2 con Visual J++ en el directorio de la práctica Inf2prac11. Copia a este directorio el fichero SimpleThread.java. Cambia el nombre al fichero anterior y llámale SimpleThread2.java. Abre el fichero SimpleThread2.java y cambia el nombre de la clase SimpleThread para que se llame SimpleThread2. Cambia este nombre en el constructor, en el programa principal y en todas las sentencias en que sea necesario. Ejecuta el programa para comprobar que todo funciona correctamente. Modifica el programa main() para que cree dos threads, llamados “Jamaica” y “Fiji”. Después de crearlos, arráncalos con el método start(). Ejecuta el programa algunas veces para ver qué isla termina primero. ¿Piensas que tiene alguna importancia el orden en el que se arrancan los threads? Ejercicio 3: Crear una aplicación que arranque tres threads Este ejercicio es completamente análogo al anterior, pero creando tres threads en lugar de dos. Llámeles “Jamaica”, “Fiji” y “Canarias”. El proyecto se llamará Ejer3 y la clase SimpleThread3. Ejercicio 4: Crear una aplicación gráfica con tres threads que simule una carrera Este ejercicio es similar al anterior, pero en este caso la carrera se va a poder seguir gráficamente en una ventana derivada de VentanaCerrable. Se trata de crear una ventana de 200x350 pixels, situada Informática 2: Práctica nº 11 página 3 a una distancia (100x100) pixels de la esquina superior izquierda de la pantalla, que responda al evento Closing en los controles de la propia ventana, y que presente el aspecto mostrado en la Figura 1. Figura 1. Carrera entre tres threads. Crea un proyecto llamado Ejer4. A continuación se muestra el “esqueleto” de la aplicación que hay que desarrollar. Este esqueleto deberá ser completado hasta que el programa funcione como el modelo, que está en el directorio Q:\Infor2\Prac11\Ejer4. El fichero Esqueleto.java, que está también en dicho directorio, te permitirá comenzar más fácilmente la aplicación. // fichero Carrera3.java import java.awt.*; import java.awt.event.*; public class Carrera3 extends VentanaCerrable implements ActionListener { // variables miembro MiPanel mp; // panel en que se harán los dibujos MiThread mt1,mt2,mt3; // las tres threads // programa principal public static void main (String[] args) { ... } // constructor public Carrera3(String titulo) { super(titulo); // se crea un objeto clese MiPanel ... // se crea el botón que arrancará las threads ... // se registra el event listener ... // se añade el botón dentro de un panel ... // se crean los tres threads ... } // método para gestionar el botón "Start" public void actionPerformed(ActionEvent e){ ... // se arrancan los threads } } // fin clase Carrera3 Informática 2: Práctica nº 11 página 4 // clase MiPanel que redefine el método paint() class MiPanel extends Panel { // variables miembro // posición de cada corredor y de la meta public int x1, x2, x3, finish; // constructor public MiPanel () { ... } // redefinir el método paint() public void paint(Graphics g) { ... // segundo corredor ... // tercer corredor ... // se dibuja la meta ... } } // fin clase MiPanel // clase MiThread que define el método run() class MiThread extends Thread { // Variable miembro // referencia a MiPanel para poder dar valor a x1, x2 y x3 MiPanel mp; // constructor public MiThread(String str, MiPanel mip) { ... } // método run() public void run() { boolean running = true; // se obtiene la posición de la meta del objeto m int finish = mp.finish; // condición para terminar el método run() while(running){ // se para el thread unos milisegundos try { sleep((int)(Math.random()*500)); } catch (InterruptedException e) {} ... // habiendo cambiado un móvil, se vuelve a dibujar ... } // se avisa que el corredor ha llegado a la meta System.out.println("El corredor " + getName()+" ha llegado!"); } // fin run() } // fin clase MiThread Como ya se ha dicho, como modelo de este ejercicio pueden utilizarse los ficheros *.class que están en el directorio q:\Infor2\Prac11\Ejer4. Informática 2: Práctica nº 11 Ejercicio 5: página 5 Nodificar el ejercicio anterior para simular una carrera con prioridades Este ejercicio es muy similar al anterior. Crea un proyecto llamado Ejer5 y copia en él el fichero Carrera3.java. Cámbiale el nombre a Carrera3Prioridades.java. Haz los cambio de nombre pertinentes dentro del fichero y comprueba que todo funciona correctamente. Para ver el efecto de las distintas prioridades se deben hacer los siguientes cambios: 1. En vez de esperar cada thread un número de milisegundos aleatorio (lo cual siempre da una oportunidad a las demás threads para ejecutarse, sea cual sea su prioridad), la espera se realizará de forma activa, por ejemplo incrementando una variable entera un millón de veces. De esta forma los distintos threads tendrán que disputarse el tiempo de la CPU y el efecto de las prioridades será patente. 2. Después de crear los objetos de la clase thread asígnales tres prioridades diferentes, como por ejemplo los valores: Thread.NORM_PRIORITY-2, Thread.NORM_PRIORITY Thread.NORM_PRIORITY+2. Esto se puede hacer con el método setPriority(). Ejecuta el programa y comprueba la diferencia entre las prioridades anteriores y el asignar a todos los threads la misma prioridad, por ejemplo Thread.NORM_PRIORITY. Como modelo de este ejercicio pueden utilizarse los ficheros *.class que están en el directorio q:\Infor2\Prac11\Ejer5. Ejercicio 6: Crear la aplicación Reloj Este último ejercicio, cuyo proyecto se llamará Ejer6, está destinado a mostrar cómo interaccionan dos threads que continen dos tipos de tareas muy diferentes: 1. Una con una gran cantidad de cálculos, y 2. Otra con muy poco trabajo pero que debe ser muy puntual (un reloj que tiene que mostrar cada segundo que pasa). En estos casos es conveniente dar la mínima prioridad al thread que tiene la tarea más pesada. Es muy poco significativo que la tarea pesada tarde unos milisegundos más y a cambio se consigue que el reloj funcione correctamente. La Figura 2 muestra la ventana principal de esta aplicación. En la parte superior hay una barra que va creciendo de pixel en pixel contando de 0 a un millón cada vez que crece. En el centro se muestra un rótulo que indica el tiempo en segundos desde el comienzo de la ejecución. Figura 2. Ventana de la aplicación Reloj. Pueden utilizarse como modelo los ficheros *.class de este ejercicio, que están en el directorio q:\Infor2\Prac11\Ejer6. Informática 2: Práctica nº 11 página 6