1
CONTENIDO
Revisión del Concepto de Hilo
Técnicas de Creación de Hilos
Ciclo de Vida. Control de Hilos
Prioridades
Hilos y Sistemas Operativos
BIBLIOGRAFÍA RECOMENDADA:
[Eck02] Eckel, B. Thinking in Java. Prentice Hall. 2002
[Göe06] Göetz et al. Java Concurrency in Practice, 2006
[Oaks04] Oaks & Wong. Java Threads. O`Reilly, 2004.
TEMA 3: Creación y Control De Threads en Java
2
Dentro de un proceso, el control suele seguir un hilo de ejecución, que comienzacon main, continúa con el resto de las instrucciones, y termina con el proceso.
Java soporta varios hilos de ejecución y por tanto, los programas de Java puedencrear dentro de sí mismos varias secuencias de ejecución concurrentes.
A diferencia de los procesos concurrentes, que son independientes, los hilos de unmismo procesocomparten el espacio de direcciones virtuales, y los recursos del s.o.
Por tanto, cada hilo tiene acceso a los datos y procedimientos del proceso, pero poseen supropio contador de programa y pila de llamadasa procedimientos.
Los problemas que aparecen con una concurrencia multihilo son los habituales:exclusión mutua y sincronización, y con menor importancia, esquema de prioridades e interbloqueos.
Se pueden tener hilos de dos formas: herencia de la clase Thread o implementación de la interfaz Runnable.Revisión del Concepto de Hilo (Thread)
Thread Clase Extendida
extends
© Antonio Tomeu Creación y Control de Thread en Java
3
Estamos en programación
concurrente… y toca usar hilos
Se optimiza el uso de la CPU
Se modelan mejor determinados
problemas… o no
El problema no admite otra solución
razonable, por ejemplo
programar un servidor decente
diseñar un GUI interactivo
Razones para Usar Hilos…
© Antonio Tomeu Creación y Control de Thread en Java 4
Thread void run() void start() ... Thread() Thread(Runnable target)
subclase
implementación
MiThread void run() { ... } RunnableObject void run()//imp. { ... } parámetroRunnable
implements
void run()API Java paraThreads: Marco General
extends
5
public class Thread extends Object
implements Runnable {
public Thread();
public Thread(String name);
public Thread(Runnable target);
public Thread(Runnable target,
String name);
public Thread(Runnable target,
String name, long stackSize);
public void run();
public void start();
public void join()
...
}
Clase Thread: API Básica
© Antonio Tomeu Creación y Control de Thread en Java 6
class Ejemplo_Hilos1
extends Thread
{
public Ejemplo_Hilos1 (int Tope) //constructor {T = Tope;}
public void run () //sobreescritura del metodo run
{
for (int i = 1; i <= T; i++)
System.out.println (i); //aquí comportamiento del } //hilo deseado private int T ;
}
Concurrencia con Hilos por Herencia de la clase
Thread
7 class Prueba_Hilo1 //Hace uso de la clase anterior {
public static void main (String [] args) throws InterruptedException
{
Ejemplo_Hilos1 Hilo1 = new Ejemplo_Hilos1 (5);
Ejemplo_Hilos1 Hilo2 = new Ejemplo_Hilos1 (15);
Hilo1.start (); //Ahora se lanzan ambos hilos... Hilo2.start (); //con apertura de co-rutina
Hilo1.join ();
Hilo2.join (); // y cierre de co-rutina System.out.println ("Hilos terminados"); }
}
© Antonio Tomeu Creación y Control de Thread en Java 8
Secuencia Temporal de Co-rutina con Hilos
Tiempo
E
J
E
C
U
C
I
Ó
N
Programa principal Hilo 1 Hilo 2Condición
de Espera
(join)
© Antonio Tomeu Creación y Control de Thread en Java
9
Dado el código anterior, incremente el número de hilos y
el número de vueltas que cada hilo da. Recompile y
ejecute.
¿Observa entrelazado en la salida?
Continúe aumentando el número de hilos (por ejemplo
definiendo un array de hilos)
¿Dónde está el límite práctico al número de hilos (% uso
de CPU próximo al 100%)?
Escriba un “hola mundo” concurrente.
EJERCICIOS
© Antonio Tomeu Creación y Control de Thread en Java 10
Revisitando incConcurrente.java
Descárguelo (Tema 1)
Array de Threads
Dato común: n
Variables static
permiten compartir
memoria entre threads
Secciones críticas
Condición de
Concurso
Resultados no
consistentes
Modelo de Memoria de Java© Antonio Tomeu Creación y Control de Thread en Java
public class Hola_Adios extends Thread {
public Hola_Adios (String Palabra) {Cadena = Palabra;}
private void otrometodo()
{System.out.println (“otro metodo”);} public void run ()
{ for (;;)
System.out.println (Cadena);
this.otrometodo(); // run puede invocar otros metodos de la clase
Integer p = new Integer(3); //o crear los objetos que necesita
}
public static void main (String [] args) {
new Hola_Adios ("Hola").start (); new Hola_Adios ("Hola").start (); new Hola_Adios ("Hola").start (); new Hola_Adios ("Adios").start (); }
Escriba un hilo que muestre pares o impares, según se indique en
el constructor, un número dado de veces, que también se indicará
en el constructor. Llame a la clase ParImpar.java
Escriba ahora un código que hago uso de la clase anterior. Llámelo
Usa_ParImpar.java
Observe el entrelazado.
Aloje una variable compartida en una clase llamada
Critica.java
. Provea métodos para incrementar la variable y
para mostrar su contenido. ¿Habría condiciones de concurso?
Escriba ahora hilos que utilicen un objeto común de esa clase.
Láncelos en un código aparte. ¿Observa algo raro?
Aunque no lo observe ¿Qué puede ocurrir potencialmente?
Realmente ¿hacía falta la clase Critica.java?
13
public interface Runnable
{
public void run();
}
Es una interface de java.lang
Cualquier clase X que la implemente expresa
ejecución concurrente
Objetos de la clase X son parámetros del
constructor de Thread
Concurrencia con Hilos por Implementación de la
Interfaz Runnable
© Antonio Tomeu Creación y Control de Thread en Java 14
public class X
implements Runnable {
... public void run()
{ //codigo concurrente } } A = new X() Objeto clase X contiene código concurrente H = new Thread(A) Código concurrente en ejecución A H H.start() A
© Antonio Tomeu Creación y Control de Thread en Java
15
public class UsoRunnableimplements Runnable
{
private String Cadena;
public UsoRunnable(String Palabra) {Cadena=Palabra;}
public void run()
{ for(;;)
System.out.println(Cadena); }
public static void main(String[] args) {
UsoRunnable Hilo1 = new UsoRunnable("Hola");
UsoRunnable Hilo2 = new UsoRunnable("Adios");
new Thread(Hilo1).start();
new Thread(Hilo2).start(); }
}
Concurrencia con Hilos por Implementación de la
Interfaz Runnable
© Antonio Tomeu Creación y Control de Thread en Java 16
/*Otra forma de crear hilos concurrentes dandoles nombre *@author Antonio J. Tomeu
*/
public class UsoRunnable2implements Runnable
{
private int Iter;
public UsoRunnable2(int Dato) {Iter = Dato;}
public void run() {
for(int i=1;i<=Iter;i++) System.out.println("Trabajando"); }
public static void main(String[] args) throws InterruptedException {
Runnable HiloA = new UsoRunnable2(100); Runnable HiloB = new UsoRunnable2(200); Runnable HiloC = new UsoRunnable2(100);
© Antonio Tomeu Creación y Control de Thread en Java
17
//version del constructor Thread crea hilo con un nombre Thread A = new Thread(HiloA, "Mi Hilo");
Thread B = new Thread(HiloB, "Tu Hilo"); //sin nombre Thread C = new Thread(HiloC);
A.start(); B.start(); A.join(); B.join(); C.join();
//metodo getName() de objetos de la clase Thread devuelve el nombre //del hilo
System.out.println(A.getName()); System.out.println(B.getName());
//no tenia nombre, pero se le dio uno en tiempo de ejecucion. System.out.println(C.getName());
} }
© Antonio Tomeu Creación y Control de Thread en Java 18
Inconveniente de heredar de Thread: No permite heredar
de otras clases.
Alternativa
de creación de hilos: Implementación de la
interfaz Runnable
Sobreescribir siempre el método run
Los objetos que implementan Runnable deben ser
lanzados
explícitamente
Se hace creando un objeto Thread cuyo parámetro es un
objeto Runnable
Luego se llama al método start del objeto Thread
creado
Técnica recomendada: implementar la interfaz
mparativa de Métodos de Multithreading
19
Escriba ahora código de hilo que implemente la interfaz Runnable.
Déle al hilo acceso compartido a una variable común. Llámelo
hiloRunn.java
Escriba un programa que haga uso de los hilos anteriores. Llámelo
usahilRunn.java. Verifique la sobreescritura.
Escriba código de hilo para el lanzamiento de una co-rutina. Cada
hilo deberá desplegar su nombre. Desarrolle una versión con cada
herramienta de creación de hilos. Llámelas Co_rutina_T.java y
Co_rutina_I.java
Inspeccione el API de la clase Thread. En C/C++, el API de
pthread.h
incluye herramientas de sincronización mediante
mutex. ¿Puede decirse lo mismo de la clase Thread en java?
Finalmente, desarrolle una versión en java del algoritmo de Lamport
para dos procesos utilizando hilos heredados de Thread .
EJERCICIOS
© Antonio Tomeu Creación y Control de Thread en Java 20
Otras técnicas de creación de Threads
Pool de Threads
Permiten reutilizar hilos
Deben ajustarse (tuning) según aplicación
Análisis en Tema 6
© Antonio Tomeu Creación y Control de Thread en Java
21 Hilo Inexistente Hilo nuevo Ejecutable Bloqueado Muerto start()
new Thread(), new Thread(Runnable t)
recolección de basura wait(), join() notify(), notifyAll() destroy()-fin_ejecución //derogado destroy()//derogado destroy()//derogado Hilo Inexistente
Objetos Thread: Ciclo de Vida
22
CLASEThread: API DE CONTROL
Un hilo t1 puede se controlado por otro hilo t0 (normalmente el hilo padre) a través de los siguientes métodos de la clase Thread.
Pasa a t1 de en ejecución a listo. t1.yield()
Suspende a t1 durante t milisegundos. t1.sleep(int t)
Mata a t1. DEROGADO
t1.destroy ()
Suspende a t0 y espera a que termine t1. t1.join ()
Envía una señal a t1. t1.interrupt ()
Hace que t1 vaya de bloqueado a listo. DEROGADO
t1.resume ()
Envía a t1 de listo/en ejecución a bloqueado. t1.suspend ()
Comprueba si el hilo t1 está vivo. t1.isAlive()
Mata al hilo t1. DEROGADO
t1.stop ()
Lanza el hilo t1 t1.start ()
Determina si t0 tiene permiso para controlar a t1.
t1.checkAccess()
Comportamiento Método
© Antonio Tomeu Creación y Control de Thread en Java
import java.io.*; Import java.util.*;
public class Control extends Thread {
//No declara constructor explicito. Usa el disponible por defecto public void run()
{
for(;;)
System.out.println("Trabajando"); }
public static void main(String[] args) throws IOException
{ int c;
//usando el constructor implicito Control Hilo = new Control(); Hilo.start();
CONTROL DE Threads: Ejemplo con Métodos Derogados
for(int i=1; i<=100; i++) //entrelazado de instrucciones System.out.println("Hola soy el padre");
Hilo.suspend(); //USO DE METODO DEROGADO, HILO PADRE SUSPENDE A HIJO .
System.out.println("Hijo suspendido"); //Ahora reactivamos al hijo, que pasa a listo. System.out.println("Pulsa 1 para despertar al hijo"); do {
c=System.in.read(); }
while(c != -1);
Hilo.resume(); //USO DE METODO DEROGADO, PASA A LISTO A HIJO
//un poquito de interfoliacion otra vez. for(int i=1; i<=100; i++)
System.out.println("Hola soy el padre");
Hilo.stop(); //USO DE METODO DEROGADO, PADRE PARA AL HIJO
} }
25
import java.io.*;
public class AutoControl extends Thread {
private int Vueltas; public AutoControl(int Dato) {Vueltas=Dato;}
public void run()
{ //el uso de sleep exige capturar la posible excepcion. try{
for(int i=1; i<=Vueltas; i++){ System.out.println(i);
if(i==25){//los hilos se suspenden en la iteracion 25 System.out.println("Suspension durante dos segundos"); int timeout = 1000;
sleep(timeout);
System.out.println("Continuando"); }//if
}//for
}catch (InterruptedException e) {return;} }
© Antonio Tomeu Creación y Control de Thread en Java
CONTROL DE Threads: Ejemplo de Replanificación Voluntaria (sleep)
26
public static void main(String[] args) {
new AutoControl(50).start(); new AutoControl(150).start(); }
}
© Antonio Tomeu Creación y Control de Thread en Java
27 public class replaniYield
extends Thread {
private boolean hY;//indicara si el hilo cede prioridad o no… private int v;
public replaniYield(boolean hacerYield, int vueltas) {hY = hacerYield; v = vueltas;}
public void run() {
for(int i=0; i<v; i++)
if(i==20&&hY==true){this.yield();}//indica cesion de prioridad… else System.out.println("Hilo "+this.getName()+" en iteracion "+i); }
public static void main(String[] args) {
replaniYield h0 = new replaniYield(false, 50); replaniYield h1 = new replaniYield(false, 50);
replaniYield h2 = new replaniYield(true , 50); //cedera prioridad y h0.setName("1-NoYield"); //sera o no considerarda h1.setName("2-NoYield"); h2.setName("3-SIYield");
h0.start(); h1.start(); h2.start(); }
}
CONTROL DE Threads: Ejemplo de Cesión de Prioridad Voluntaria (yield)
© Antonio Tomeu Creación y Control de Thread en Java 28
CONTROL DE PRIORIDAD
Prioridad hilo hijo igual a la de hilo padre
La prioridad tiene sentido
exclusivamente
en el ámbito de la
JVM… aunque se mapea a los hilos de sistema (
OJO: EL
MAPPING NO ES RIGUROSO=>INVERSIONES DE
PRIORIDAD
)
Clase Thread:esquema de diez niveles de prioridad
Ver la prioridad de un hilo:
public int getPriority()
Alterar la prioridad de un hilo:
public void setPriority(int p) (1<=p<=10)
© Antonio Tomeu Creación y Control de Thread en Java
29
package java.lang;
public class Thread implements Runnable
public final static int Thread.MIN_PRIORITY;
public final static int Thread.NORM_PRIORITY;
public final static int Thread.MAX_PRIORITY;
public void setPriority(int prioridad);
public int getPriority():
CLASE Thread: API DE CONTROL DE PRIORIDAD
© Antonio Tomeu Creación y Control de Thread en Java 30
PLANIFICACIÓN BASADA EN PRIORIDADES
Valor prioridad en JVM indica al planificador
del S.O qué hilos van primero…
pero
no es un contrato absoluto
entre
ambos ya que depende:
de la implementación de la JVM
del S.O. subyacente
del mapping prioridad jvm-prioridad s.o.
31 public class Prioridades extends Thread
{
private long dato;
private static int prio = 4; //atributo de clase comun a instancias public Prioridades (long n){dato=n;}
private long fac(long n) {
if (n == 0) return 0; else if (n == 1) return 1;
else return(fac(n-1)*n); }
public void run() {
//this.setPriority(prio++); //ejecutar con y sin el ajuste de prioridad System.out.println("El factorial de "+dato+" es "+fac(dato)); }
public static void main(String[] args) {
new Prioridades(10).start(); //orden lanzamiento no es igual al orden new Prioridades(20).start(); //de ejecución… pero
new Prioridades(30).start(); //¿ajustando las prioridades? new Prioridades(40).start();
new Prioridades(50).start(); new Prioridades(60).start(); }
}
© Antonio Tomeu Creación y Control de Thread en Java 32
import java.util.*; import java.text.*;
public class Trabajo implements Runnable { long n;
String id;
private long fib(long n) { if (n == 0)
return 0L; if (n == 1)
return 1L;
return fib(n - 1) + fib(n - 2); }
public Trabajo(long n, String id) { this.n = n;
this.id = id; }
public void run() { Date d = new Date();
DateFormat df = new SimpleDateFormat("HH:mm:ss:SSS"); long startTime = System.currentTimeMillis(); d.setTime(startTime);
System.out.println("Iniciando trabajo " + id + " a las " + df.format(d)); fib(n);
long endTime = System.currentTimeMillis(); d.setTime(endTime);
System.out.println("Acabando trabajo " + id + " a las " + df.format(d) + " tras " + (endTime - startTime) + " milliseconds");
} }
33
public class ThreadTest {
public static void main(String[] args) { int nHilos = Integer.parseInt(args[0]); long n = Long.parseLong(args[1]); Thread t[] = new Thread[nHilos]; for (int i = 0; i < t.length; i++) {
t[i] = new Thread(new Trabajo(n, "Trabajo " + i)); t[i].start();
}
for (int i = 0; i < t.length; i++) { try {
t[i].join();
} catch (InterruptedException ie) {} }
} }
© Antonio Tomeu Creación y Control de Thread en Java 34
public class ThreadTestNuevaPrioridad { public static void main(String[] args) {
int nHilos = Integer.parseInt(args[0]); long n = Long.parseLong(args[1]); Thread t[] = new Thread[nHilos]; for (int i = 0; i < t.length; i++) {
t[i] = new Thread(new Trabajo(n, "Trabajo " + i));
t[i].setPriority((i % 10)+1);
t[i].start(); }
for (int i = 0; i < t.length; i++) { try {
t[i].join();
} catch (InterruptedException ie) {} }
} }
© Antonio Tomeu Creación y Control de Thread en Java
Compile y ejecute los códigos anteriores
¿Observa inversiones de prioridad?
¿A qué cree que se deben?
Desarrolle ahora código de hilo
sincronizado basado en prioridades (p.e. un
hilo incrementa un dato y otro lo muestra,
en ese orden)
¿Es una estrategia válida de
sincronización?
¿Y con una co-rutina?
EJERCICIOS
El s.o. conoce el número de hilos que
usa la JVM
Se aplican uno-a-uno. (JVM a Win)
El secuenciamiento de hilos java está
sujeto al del s.o.
Se aplican 10 prioridades en la JVM
sobre 7 en el s.o.+5 prioridades de
secuenciamiento
37
MAPPING PRIORIDADES JVM A WIN32
THREAD.PRIORITY_TIME_CRITICAL 10 (Thread.MAX_PRIORITY) THREAD.PRIORITY_HIGHEST 9 THREAD.PRIORITY_HIGHEST 8 THREAD.PRIORITY_ABOVE_NORMAL 7 THREAD.PRIORITY_ABOVE_NORMAL 6 THREAD.PRIORITY_NORMAL 5 (Thread.NORM_PRIORITY) THREAD.PRIORITY_BELOW_NORMAL 4 THREAD.PRIORITY_BELOW_NORMAL 3 THREAD.PRIORITY_LOWEST 2 THREAD.PRIORITY_LOWEST 1 (Thread.MIN_PRIORITY) THREAD.PRIORITY_IDLE 0 Prioridad Win32 Prioridad Java
© Antonio Tomeu Creación y Control de Thread en Java 38
Pero recuerde…
En general, hilos de baja prioridad
obtendrán acceso al procesador
cuando los de alta prioridad estén en
espera.
La
prioridad es poco significativa
cuando todos los hilos compiten por el
procesador.
MAPPING HILOS JVM-HILOS NATIVOS DE WIN32
© Antonio Tomeu Creación y Control de Thread en Java
39
Núcleos recientes implementan Native
Posix Thread Library
Aplican hilos JVM a hilos del núcleo
uno-a-uno bajo el modelo de Solaris
La
prioridad java es un factor muy
pequeño
en el cálculo global del
secuenciamiento
MAPPING HILOS JVM-HILOS NATIVOS DE LINUX
© Antonio Tomeu Creación y Control de Thread en Java 40
MAPPING PRIORIDADES JVM A PRIORIDADES
DE HILOS LINUX
-5 10 (Thread.MAX_PRIORITY) -4 9 -3 8 -2 7 -1 6 0 5 (Thread.NORM_PRIORITY) 1 4 2 3 3 2 4 1 (Thread.MIN_PRIORITY) no contemplado 0Prioridad Linux (nice value) Prioridad Java
© Antonio Tomeu Creación y Control de Thread en Java
41
Solo como root
(o con privilegios
equivalentes, setuid)
Parametrizar a la JVM indicando que
tenga en cuenta las prioridades.
RESTRICCIONES DEL MAPPING PRIORIDADES JVM A LINUX
© Antonio Tomeu Creación y Control de Thread en Java 42
La actual especificación de la JVM
no
establece un modelo de planificación por
prioridades
El comportamiento puede y debe variar en
diferentes máquinas
En secuencias de tareas estrictas, no es
posible planificar con prioridades
Aunque sí con co-rutinas, a nivel básico
CONTROL DE PRIORIDAD: CONCLUSIONES
43