Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
MDULO: PSP
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
2. Hilos en JAVA
2.1.
2.2.
2.3.
2.4.
2.5.
2.6.
Clase Thread
Clases e interfaces relacionadas con los Hilos
Estados de ejecucin de un hilo
Mecanismos de control de hilos
Mecanismos de prioridad en los hilos
Sincronizacin de hilos
Pgina 1 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Pgina 2 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
5. ...
Por otro lado, la utilizacin de hilos tiene sus desventajas:
1. No todos los lenguajes de programacin soportan multihilo
2. Hay que controlar los problemas relacionados con la comunicacin y sincronizacin
de hilos, cuando stos comparten recursos:
Inanicin: un hilo no puede utilizar los recursos porque otro no los libera.
Problemas de interbloqueo entre hilos (Deadlock), 2 hilos se bloquean
mutuamente esperndose el uno al otro sin avanzar ninguno de los 2.
Acceso a los recursos compartidos por 2 hilos, llamados recursos crticos.
Zonas de exclusin mutua, secciones crticas: trozo de cdigo donde solo se
ejecuta un hilo en un momento dado...
Condiciones de carrera: el resultado de la ejecucin de un proceso depende
del orden en que se ejecuten los hilos.
Errores de inconsistencia en la memoria compartida.
2. Hilos en JAVA
Java dispone de varios mecanismos para la programacin paralela. Por ejemplo la clase
Thread perteneciente a la API java.lang; y un conjunto de herramientas para
sincronizar hilos, clases... pertenecientes a la API java.util.concurrent. En este punto
nos vamos a centrar en la clase Thread.
Pgina 3 de 23
1.
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Thread: La clase Thread es la clase que encapsula todo el control necesario para
implementar hilos de ejecucin. La clase Thread tiene sus mtodos descritos en:
http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
de
los
cuales
destacamos los siguientes mtodos de clase (mtodos estticos que deben
llamarse de forma directa en la clase Thread):
a. currentThread(): devuelve el objeto Thread que representa al hilo que se
est ejecutando.
b. yield(): este mtodo hace que el sistema pase del hilo de ejecucin actual al
hilo siguiente disponible. As los hilos de menor prioridad no sufren
inanicin.
c. sleep(long): este mtodo pone a dormir el n de milisegundos indicado como
parmetro.
Entre los mtodos de instancia destacamos:
d. start(): inicializa el hilo, solo se puede llamar una vez
e. run(): Este mtodo contiene el cuerpo de ejecucin del hilo
f. setPriority(int): asigna al hilo la prioridad pasada como parmetro
g. getPriority(): devuelve la prioridad del hilo en ejecucin
h. setName(String): permite asignar un nombre al hilo
i. getName(): devuelve el nombre del hilo
2.
3.
Pgina 4 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
En ejecucin (running) Solo estn ejecutndose los hilos inicializados que estn
utilizando la CPU. El sistema operativo selecciona de entre los hilos en estado
runnable, los que pasan al estado running y empieza a ejecutar el mtodo run().
Bloqueado (Not running): El estado not running se aplica a todos los hilos que
estn parados por alguna razn. Cuando un hilo est en este estado, no se le
asigna tiempo de CPU pero est listo para ser usado y es capaz de volver al estado
runnable en un momento dado. Los hilos pueden pasar al estado not running a
travs de varias vas, por ejemplo:
Si est dormido, porque se ha llamado al mtodo sleep()
Si est esperando, a travs del mtodo wait()
Cuando el subproceso est bloqueado en una operacin Entrada/Salida.
En este caso se pueden heredar los mtodos y variables de la clase padre. Pero, una
misma subclase solamente puede extender o derivar una vez de la clase padre Thread.
Pgina 5 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Pgina 6 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
El inicio de los hilos se realiza con el mtodo start(). Este mtodo crea los recursos
necesarios para que el hilo pueda ejecutarse, lo incorpora a la lista de procesos
disponibles para ejecucin y llama al mtodo run(). Un hilo solo puede iniciarse una
vez, por tanto este mtodo no se puede llamar ms de una vez.
2.4.3. Ejecucin de un hilo
Una vez creado e inicializado un hilo, ste pasa del estado runnable al estado runnig
cuando el scheduler lo selecciona y ejecuta el mtodo run().
En el mtodo run se implementa el cdigo correspondiente a la accin o tarea que el
hilo debe desarrollar. Los hilos trabajan con el mtodo run() sin argumentos.
Cuando ejecutamos un programa con varios hilos de ejecucin podremos comprobar
que el comportamiento de los hilos no es predecible. Los hilos no necesariamente se
ejecutan en el mismo orden en que fueron introducidos. El scheduler controla el
tiempo de CPU de cada hilo y lo nico seguro es que cada hilo se ejecutar hasta
terminar su tarea. Si tenemos varios hilos en ejecucin y queremos saber cual se est
ejecutando en un momento dado, el mtodo Thread.currentThread.getName()
devuelve el nombre del hilo en ejecucin.
2.4.4. Suspensin o bloqueo de hilos
El S.O. asigna tiempos de CPU a los hilos inicializados con el mtodo start(). Pero a
veces es necesario detener temporalmente uno de ellos, cambiando su estado a
bloqueado, o not running. Para detener un hilo tenemos varias opciones:
i.
ii.
A travs del mtodo wait() o wait(n ms) el hilo de ejecucin espere en estado
dormido hasta que se le notifique que contine. En wait() utilizaremos notify() o
notifyAll() para despertar el hilo. El mtodo notify informa a un hilo en espera de
que contine con su ejecucin. El mtodo notifyAll es similar a notify excepto
que se aplica a todos los hilos en espera. En wait (n ms) el hilo queda esperando
hasta que sea despertado a travs de notify o notifyAll o hasta el n de ms
indicado. Estos mtodos se suelen utilizar para codificar bloques de
sincronizacin o secciones crticas, donde es necsario sincronizar hilos que
necesitan acceder a un mismo recurso: dato, archivo, base de datos... por
ejemplo: el problema del productor-consumidor.
Pgina 7 de 23
iii.
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Este ejemplo muestra una aplicacin JAVA de creacin de hilos extendiendo la clase
Thread:
Ejemplo 1:
archivo Hilo1.java
public class Hilo1 extends Thread
{
private String nombre;
public Hilo1(String nombre)
{
this.nombre=nombre;
}
/** El mtodo run genera un n aleatorio de ms, que ser el tiempo que el hilo estar
dormido antes de visualizar su nombre y el retardo o tiempo que ha permanecido
dormido**/
public void run()
{
try{
int x=(int)(Math.random()*5000);
Thread.sleep(x);
System.out.println("Soy "+nombre+ " ("+x+")");
}
catch (Exception ex){
}
}
public static void main(String[] args)
{
Hilo1 t1 = new Hilo1("Pedro");
Hilo1 t2 = new Hilo1("Pablo");
Hilo1 t3 = new Hilo1("Juan");
t1.start();
//mtodo que inicializa el hilo y llama a run()
t2.start();
t3.start();
}
}
El siguiente ejemplo muestra una aplicacin JAVA creando hilos con Runnable:
Ejemplo 2:
archivo hilo2.java
Pgina 8 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Finalizacin de un hilo
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
A veces necesitamos esperar a que finalice un hilo o grupo de ellos para seguir
adelante con otras tareas, pero como los hilos se ejecutan concurrentemente con el
programa que los lanz, esto puede provocar salidas de programa no deseadas
relacionadas con el orden de ejecucin de las acciones programadas.
Si queremos que una accin se realice despus de finalizar un hilo hay que esperar a
que ste acabe, utilizando el mtodo join(). Este mtodo puede incluir como
parmetro el n de ms que queremos esperar a que acabe el hilo.
En el siguiente ejemplo, para que el mensaje de cada hilo se visualice en el mismo
orden que se introdujo, ser necesario llamar al mtodo join().
Ejemplo3:
Pgina 10 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
archivo Hilojoin.java
public class Hilojoin extends Thread
{
private String nombre;
public Hilojoin(String nombre)
{
this.nombre=nombre;
}
public void run(){
try{
int x=(int)(Math.random()*5000);
Thread.sleep(x);
System.out.println("Soy "+nombre+ " ("+x+")");
}
catch (Exception ex){
}
}
public static void main(String[] args) throws InterruptedException {
Hilojoin t1 = new Hilojoin("Pedro");
Hilojoin t2 = new Hilojoin("Pablo");
Hilojoin t3 = new Hilojoin("Juan");
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
System.out.println("fin del proceso");
}
}
Qu pasara si no utilizsemos el mtodo join?
Pgina 11 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Cuando se crea un hilo en Java, ste hereda la prioridad de su padre, el hilo que lo ha
creado. A partir de aqu se le puede modificar su prioridad en cualquier momento
utilizando el mtodo setPriority.
La clase Thread define tres constantes que representan los niveles de prioridad
relativos para los subprocesos:
MIN_PRIORITY vale 1
MAX_PRIORITY vale 10
NORM_PRORITY
Tomando 1 el valor de la mnima prioridad y 10 el valor de la mxima prioridad.
Un ejemplo de hilo de baja prioridad es el que libera la memoria no usada que se
ejecuta en la Mquina Virtual Java. An cuando la liberacin de memoria es una tarea
muy importante, su baja prioridad evita que el procesador est ocupado demasiado
tiempo en ella, dejando a los procesos crticos el uso prioritario de la CPU. Pero si en
un momento dado la memoria se agotara, los subprocesos crticos entran en estado de
espera, dejando a la CPU que ejecute el subproceso que libera la memoria no usada (el
de menos prioridad).
Para saber el nivel del prioridad de un subproceso se usa la funcin getPriority():
int prioridad=hilo1.getPriority();
Se puede cambiar la prioridad de un subproceso respecto de otro aumentando o
disminuyendo su valor de prioridad, por ejemplo:
hilo2.setPriority(hilo1.getPriority()+1);
Se ejecutar primero el hilo de prioridad superior, y cuando ste finaliza o se convierte
en No Ejecutable, comienza la ejecucin de un hilo de prioridad inferior. El hilo
seleccionado se ejecutar hasta que:
1. Un hilo con prioridad mayor pase a ser Ejecutable.
2. Abandone, o termine su mtodo run.
Los sistemas operativos no estn obligados a tener en cuenta la prioridad de los hilos.
Cuando todos los hilos tienen la misma prioridad, y queremos que la CPU cambie de
hilo de forma equilibrada, se utiliza el mtodo yield(). Este mtodo indica a la JVM
(mquina virtual de java) que el hilo en ejecucin puede pasar del estado running a
runnable, permitiendo as que otros hilos se ejecuten.
Para probar la prioridad en hilos, vamos a partir del siguiente archivo:
Ejemplo 4:
archivo hilo.java
public class hilo extends Thread
{
Pgina 12 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Pgina 13 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
La seccin crtica debe ser mutuamente excluyente, es decir, si un hilo est ejecutando
su seccin crtica en un momento dado, ningn otro hilo puede ejecutarla hasta que
no finalice el que est usndola.
Java dispone del modificador synchronized que, aplicado a un mtodo, garantiza que
ste se ejecuta de forma excluyente. Una clase con un mtodo synchronized() se llama
monitor, (tubera, buffer) porque dentro de ste se estar monitoreando algn recurso
crtico. Un monitor implementa una seccin crtica. Para que una clase sea monitor,
todos sus mtodos de la clase deben ser synchronized.
Para codificar monitores en Java, utilizamos, adems de synchronized, los mtodos:
1. wait(): Si no se cumple la condicin, esperamos.
2. notify(): Cuando hemos entrado en la seccin crtica, y finalizamos la tarea
asignada, avisamos al hilo que haya esperando que puede entrar, si se cumple
la condicin.
3. notifyAll(): Igual que el anterior, pero notificamos a todos los hilos que hay
esperando.
Por ejemplo, si queremos que la variable c de este programa sea manipulada
correctamente, en Java habra que codificarlo as:
public class SynchronizedCounter {
private int c = 0;
public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
public synchronized int value() {
return c;
}
}
Java permite sincronizar una parte del cdigo de un mtodo. Para ello se utiliza la
palabra clave syncronized(objeto), indicando entre parntesis el objeto que se desea
sincronizar. Por ejemplo si se desea sincronizar el propio thread en una parte del
mtodo run(), el cdigo podra ser:
public void run() {
while(true) {
...
syncronized(this) { // El objeto a sincronizar es el propio thread
Pgina 14 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Pgina 15 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
cubbyhole = c;
this.numero = numero; // retardo aplicado al productor
}
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("Productor pone: " +i);
try {
sleep(numero);
} catch (InterruptedException e) {}
}
}
El consumidor, por su parte, est hambriento, consume los enteros de CubbyHole
una vez y en el mismo orden que fueron introducidos, tan pronto como estn
disponibles.
class Consumidor extends Thread {
private CubbyHole cubbyhole;
private int numero;
public Consumidor(CubbyHole c, int numero) {
cubbyhole = c;
this.numero = numero;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyhole.get();
System.out.println("Consumidor saca:"+value);
try {
sleep(numero);
} catch (InterruptedException e) {}
}
CubbyHole es la clase que se encarga de poner y extraer los datos del productor y
consumidor
class CubbyHole {
private int contents;
public int get() {
return contents;
}
Pgina 16 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Pgina 17 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Pgina 18 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
contents = value;
available = true;
notify();
}
}
Qu sucedera si estos mtodos no estuviesen sealados como seccin crtica?
Prueba este ejemplo , con el productor durmiendo, con el consumidor durmiendo,
estando los 2 durmiendo, estando los dos despiertos... y con el monitor sin
sincronizar. Observa lo que pasa con wait y notify cuando el monitor no est
sincronizado.
Cuando el Productor invoca el mtodo put(), adquiere el monitor del objeto CubbyHole
y por lo tanto el Consumidor no podr llamar a get() y se quedar bloqueado. Lo
mismo sucede cuando el Consumidor invoca a get().
public synchronized void put(int value) // El productor adquiere el monitor
{
while (available == true) {
try {
wait(); // espera que el consumidor invoque a notify
} catch (InterruptedException e) {}
}
contents = value;
available = true;
notify();
// el productor libera el monitor
}
public synchronized int get() // El consumidor adquiere el monitor
{
while (available == false) {
try {
wait();
// espera que el Productor invoque a notify
} catch (InterruptedException e) {}
}
available = false;
notify();
// el Consumidor libera el monitor
return contents;
}
}
La variable contents tiene el valor actual de CubbyHole y available indica si se puede
recuperar o no el valor. Cuando available es verdadero, el productor an no ha
acabado de producir.
Pgina 19 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Pgina 20 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Pgina 21 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/SynchronousQ
ueue.html
5. CountDownLatch, CyclicBarrier, Exchanger y Phaser: son clases sincronizadas.
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Phaser.html...
6. Callable: interfaz similar a Runnable, cuando necesitamos devolver un
resultado o lanzar una excepcion. Se utiliza con Executors y Future.
7. Executor: es una interfaz con mltiples implementaciones que permiten
controlar la ejecucin de un hilo, por ejemplo, podemos limitar el nmero de
hilos que deseamos se ejecuten concurrentemente.
8. Future: interfaz utilizado con Executor para devolver el resultado de un hilo
callable diseado para evitar problema de inconsistencia de datos en la
memoria.
9. ThreadGroup: es una forma de agrupar varios hilos bajo un nombre comn.
Pertenece a java.lang.
10. Thread factory: es una forma de implementar una fbrica de objetos hilo
Estos se describen en: http://docs.oracle.com/javase/7/docs/api/
11. Fork-join: es una implementacin de la interfaz Executor que resuelve las
tareas dividindolas en ms pequeas, de forma recursiva.
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinWorke
rThread.html
12. ...
EJERCICIOS OBLIGATORIOS
1. Relacin 2
2. Relacin 3
EJERCICIOS OPCIONALES
1. Implementa el ejemplo del productor consumidor con otra herramienta de
sincronizacin de Java distinta a los monitores. Crea un documento pdf con la
informacin recopilada y el archivo fuente depurado y documentado. Enva el
documento por correo electrnico antes del 31 de Octubre.
2. Investiga sobre otros problemas de concurrencia, descrbelos e intenta
codificar en Java alguno de ellos. Crea un documento pdf con la informacin
recopilada y el archivo fuente depurado y documentado. Enva el documento
por correo electrnico antes del 31 de Octubre.
Pgina 22 de 23
CURSO 2013-2014
TEMA 2
GRUPO: 2 DAM
PROGRAMACIN MULTIHILO
Webgrafa:
1. http://www.youtube.com/watch?v=vS2Mg7Vxwn0, videotutorial de Jess Conde
sobre Threads en JAVA.
2. http://www.oracle.com/technetwork/java/javase/downloads/index.html descarga de
JDK y Netbeans ltima versin.
web en ingls con
3. http://www.isr.umd.edu/~austin/ence489c.d/threads.html
ejemplos de hilos: sincronizacin, animacin, reloj...
4. http://www.sc.ehu.es/sbweb/fisica/cursoJava/applets/threads/threads.htm
web
similar con ejemplos de hilos similares a los de la web anterior.
5. http://arquimedes.matem.unam.mx/pasados/java_profundizacion/Unidad06.htm
ejemplos de aplicaciones de los hilos: applet, animaciones..
Pgina 23 de 23