Sincronización de Threads - Universidad de La Laguna

Anuncio
Sincronización de Threads
Herramientas y Lenguajes de Programación
Universidad de La Laguna
Programa de Doctorado de Fı́sica e Informática
Escuela Técnica Superior Superior de Ingenierı́a Informática
Dpto. Sistemas Informáticos y Computación
2004-2005
Resumen
El objetivo de esta sesión es mostrar el modo de funcionamiento de los distintos
tipos de programas Java. Además se introducen los threads (hilos) y trabaja con los
principios básicos de los mismos: su sincronización y secuencialización.
1.
Ejemplo de aplicación
La programación se vuelve un poco más compleja cuando se tiene un programa con
threads. Considérese la siguiente descripción aparentemente sencilla:
Los threads A y B comparten un dato, contador. El thread A efectúa repetidamente algunos cálculos que producen un entero y lo coloca en el contador.
El thread B obtiene repetidamente el texto del contador y lo utiliza para sus
propios cálculos.
Cuando el programa esté en ejecución, el sistema alterna la ejecución de A y B. La
forma en que ocurre esto varı́a de un Sistema Operativo a otro y está completamente fuera
del control del programador.
Es posible que el sistema ejecute un thread hasta terminarlo, antes de iniciar el otro;
que ejecute tres instrucciones de un thread antes de hacer lo mismo con una del otro, e
incluso que deje un thread en medio de una instrucción, lo suspenda y empiece con el otro.
Una vez adevertido que es imposible suponer nada acerca del orden de ejecución
de dos o más threads, aparecen los siguientes escenarios:
1. El thread A se ejecuta parcialmente en la actualización del contador y luego la ejecución cambia a B. El resultado de B puede recibir basura cuando trata de inspeccionar
el contador.
2. El thread A escribe nueva información en el contador antes de que B inspeccione el
valor antiguo. Este último se pierde.
1
Herramientas y Lenguajes de Programación 04-05
2
3. El thread B recibe un valor y luego accede a contador de nuevo antes de que A haya
generado un nuevo valor. Se utiliza dos veces el valor antiguo.
El escenario 1 requiere exclusión mutua, en que no permite que dos threads tengan
acceso simultáneo al recurso compartido contador. Los escenarios 2 y 3 requieren secuencialización, en que cada thread debe esperar a que el otro termine de usar el recurso
compartido.
Es importante señalar que estos escenarios son problemáticos sólo porque los threads A
y B tienen acceso al objeto contador. Si el código que ejecutan A y B no hiciera referencia
a un objeto compartido, estos subprocesos podrı́an ejecutarse en el orden que decida el
Sistema Operativo y dicho orden no tendrı́a efecto en el resultado del programa.
Figura 1: Ejemplo del Productor/Consumidor
La aplicación PCTest.java contiene la definición de una aplicación Productor/Consumidor
en la que se instancian dos objetos: uno de tipo Productor y otro de tipo Consumidor.
Estos objetos son threads que se encargan uno de poner un valor y el otro de recogerlo
de un objeto de tipo Mostrador. El Productor genera un entero entre 0 y 9, lo almacena
en el mostrador y lo imprime. El Consumidor al contrario, consume todos los enteros del
mostrador (que es exactamente el mismo objeto en el que el productor coloca los enteros)
tan pronto como están disponibles. Ası́ pues, el productor y el consumidor de este ejemplo
comparten los datos a través del objeto de tipo Mostrador (figura 1).
El primer paso para manipular una aplicación Java es compilarla ejecutando en la lı́nea
de comandos la instrucción
>javac PCTest.java
Es importante que el nombre del fichero que contiene el código coincida exactamente
con el nombre de la clase que se declara como pública.
Al realizar este paso se obtiene el siguiente conjunto de ficheros Mostrador.class,
Productor.class, Consumidor.class, PCTest.class.
Para ejecutar la aplicación se escribe en la lı́nea de comandos
>java PCTest
Herramientas y Lenguajes de Programación 04-05
2.
3
Ejemplo de applet
El código que aparece a continuación muestra la implementación en Java del programa
que muestra en la ventana principal del navegador la frase “Hola Mundo en Java”:
/**
* Applet Hello World
*/
import java.applet.Applet;
import java.awt.Graphics;
public class AppletSimple extends Applet{
public void paint(Graphics g){
g.drawString("Hola Mundo en Java", 50, 25);
}
}
El primer paso para manipular un applet Java es compilarlo ejecutando en la lı́nea de
comandos la instrucción
>javac AppletSimple.java
Al realizar este paso se obtiene una fichero AppletSimple.class.
El fichero .class resultante de la compilación, se ha de incrustar en un fichero para
ser ejecutado por un navegador.
En este caso las etiquetas a utilizar son <APPLET> y </APPLET>.
El siguiente código HTML contine la estructura de la etiqueta para el ejemplo que nos
ocupa (está almacenado en un fichero con nombre html.html).
<html>
<head>
<title> Un applet simple </title>
</head>
<body>
<p>
A continuación está la salida del programa
</p>
<applet code="AppletSimple.class" width="300" height="100">
No hay disponible un intérprete de Java
</applet>
</body>
</html>
Nótese que en el atributo asociado code de la etiqueta <APPLET> se ha especificado
AppletSimple.class y no AppletSimple.java.
Finalmente, cuando se abre con un navegador el fichero html.html se obtiene el resultado que se muestra en la figura 2.
Herramientas y Lenguajes de Programación 04-05
4
Figura 2: Ejecución del applet en un navegador
El paquete de desarrollo que proporciona SUN también ofrece una herramienta de
visualización. Para usarla se ha de ejecutar:
>appletviewer html.html
Figura 3: Ejecución del applet con appletviewer
La herramienta appletviewer sólo muestra el applet. Ignora el código HTML en el
que está incrustado (véase la figura 3).
3.
Ejercicios
1. Compile y ejecute la aplicación Productor/Consumidor descrita en la primera sección.
2. Añada al código de la aplicación Productor/Consumidor las sentencias necesarias
para que los dos threads que se ejecutan pasen al estado de dormido durante un
intervalo aleatorio de tiempo.
Herramientas y Lenguajes de Programación 04-05
5
3. Implemente los cambios necesarios para que el programa admita la creación de más
de un thread productor y más de un thread consumidor. Ejecute el nuevo programa
lanzando varios productores y varios consumidores.
4. Implemente los cambios necesarios para que el programa se pueda ejecutar como un
applet. Para ello:
extienda la clase TextField del paquete java.awt de manera que proporcione
los métodos sincronizados setInt() y getInt() que permitan poner y recoger
un entero en el campo de texto.
El Consumidor ha de recoger los valores del campo de texto y sumarlos.
El Productor generará los enteros impares sucesivos y los coloca en el campo
de texto compartido.
El Productor indica el final de su tarea colocando el valor “-1” en el campo de
texto, mientras que el Consumidor utiliza el “-1” como señal para informar de
la suma.
Finalmente se utilizan las tres clases anteriores para crear el applet que contiene
un campo de texto extendido y un botón que al hacer click en él lanza los
threads.
public class Test extends Applet implements ActionListener {
private IntField display;
private Button startButton;
private Productor p;
private Consumidor c;
public void init() {
display = new IntField();
startButton = new Button ("Start");
p = new Productor (display);
c = new Consumidor (display);
add (display);
add (startButton);
startButton.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
p.start();
c.start();
}
}
Descargar