Estamos en programación concurrente… y toca usar hilos Se

Anuncio
Revisión del Concepto de Hilo (Thread)
TEMA 3: Creación y Control De Threads en Java
ƒ
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.
Clase Extendida
extends
© Antonio Tomeu
Creación y Control de Thread en Java 2
API Java paraThreads: Marco General
Razones para Usar Hilos…
Thread
„ 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
…
Java soporta varios hilos de ejecución y por tanto, los programas de Java pueden
crear dentro de sí mismos varias secuencias de ejecución concurrentes.
A diferencia de los procesos concurrentes, que son independientes, los hilos de un
mismo proceso comparten 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 su propio contador de programa y pila de llamadas a 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.
Thread
1
…
Dentro de un proceso, el control suele seguir un hilo de ejecución, que comienza
con main, continúa con el resto de las instrucciones, y termina con el proceso.
RunnableObject
parámetro
Thread()
Thread(Runnable target)
void run()
void start()
...
void run()//imp.
{
...
}
extends
implements
MiThread
Runnable
void run()
{
...
}
programar un servidor decente
diseñar un GUI interactivo
void run()
subclase
© Antonio Tomeu
Creación y Control de Thread en Java 3
Clase Thread: API Básica
}© Antonio Tomeu
Creación y Control de Thread en Java
4
Concurrencia con Hilos por Herencia de la clase
Thread
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()
...
implementación
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 ;
}
5
© Antonio Tomeu
Creación y Control de Thread en Java 6
1
Secuencia Temporal de Co-rutina con Hilos
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
Condición
de Espera
(join)
E
J
E
C
U
C
I
Ó
N
Hilo 1
Hilo 2
Creación y Control de Thread en Java 7
© Antonio Tomeu
„
„
„
„
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.
© Antonio Tomeu
Creación y Control de Thread en Java 9
„
„
„
„
„
„
„
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
© Antonio Tomeu
Modelo de Memoria de Java
Creación y Control de Thread en Java 10
EJERCICIOS
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 ();
}
private String Cadena;
} © Antonio Tomeu
Creación y Control de Thread en Java 8
Revisitando incConcurrente.java
EJERCICIOS
„
Tiempo
Programa principal
Creación y Control de Thread en Java 11
„
„
„
„
„
„
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?
© Antonio Tomeu
Creación y Control de Thread en Java 12
2
Concurrencia con Hilos por Implementación de la
Interfaz Runnable
public interface Runnable
{
public void run();
}
… Es una interface de java.lang
clase X que la implemente expresa
ejecución concurrente
… Objetos de la clase X son parámetros del
constructor de Thread
public class X
implements Runnable
{
...
public void run()
{
//codigo concurrente
}
}
A = new X()
Objeto clase
X
A
contiene
código
concurrente
H = new Thread(A)
… Cualquier
© Antonio Tomeu
Creación y Control de Thread en Java 13
Concurrencia con Hilos por Implementación de la
Interfaz Runnable
public class UsoRunnable implements 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();
}
H
H.start()
Código
concurrente
en ejecución
© Antonio Tomeu
A
Creación y Control de Thread en Java 14
/*Otra forma de crear hilos concurrentes dandoles nombre
*@author Antonio J. Tomeu
*/
public class UsoRunnable2 implements 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 15
© Antonio Tomeu
Creación y Control de Thread en Java 16
mparativa de Métodos de Multithreading
//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());
}
}
ƒ 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
© Antonio Tomeu
Creación y Control de Thread en Java 17
© Antonio Tomeu
Creación y Control de Thread en Java 18
3
Otras técnicas de creación de Threads
EJERCICIOS
„
„
„
„
„
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 .
© Antonio Tomeu
Pool de Threads
„ Permiten reutilizar hilos
„ Deben ajustarse (tuning) según aplicación
„ Análisis en Tema 6
„
Creación y Control de Thread en Java 19
© Antonio Tomeu
Objetos Thread: Ciclo de Vida
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.
ƒ
Hilo Inexistente
Método
new Thread(), new Thread(Runnable t)
destroy()//derogado
start()
destroy()-fin_ejecución
//derogado
wait(), join()
notify(), notifyAll()
Muerto
Bloqueado
destroy()//derogado
recolección de basura
Comportamiento
t1.checkAccess()
Determina si t0 tiene permiso para controlar a
t1.
t1.start ()
Lanza el hilo t1
t1.stop ()
Mata al hilo t1. DEROGADO
t1.isAlive()
Comprueba si el hilo t1 está vivo.
t1.suspend ()
Envía a t1 de listo/en ejecución a bloqueado.
t1.resume ()
Hace que t1 vaya de bloqueado a listo. DEROGADO
t1.interrupt ()
Envía una señal a t1.
t1.sleep(int t)
Suspende a t1 durante t milisegundos.
t1.yield()
Pasa a t1 de en ejecución a listo.
t1.join ()
Suspende a t0 y espera a que termine t1.
t1.destroy ()
Mata a t1. DEROGADO
Hilo nuevo
Ejecutable
Creación y Control de Thread en Java 20
Hilo Inexistente
© Antonio Tomeu
21
Creación y Control de Thread en Java 22
CONTROL DE Threads: Ejemplo con Métodos Derogados
import java.io.*;
Import java.util.*;
public class Control extends Thread
{
//No declara constructor explicito. Usa el disponible por defecto
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
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();
© Antonio Tomeu
}
}
Creación y Control de Thread en Java 23
© Antonio Tomeu
Creación y Control de Thread en Java 24
4
CONTROL DE Threads: Ejemplo de Replanificación Voluntaria (sleep)
public static void main(String[] args)
{
new AutoControl(50).start();
new AutoControl(150).start();
}
}
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 25
© Antonio Tomeu
CONTROL DE Threads: Ejemplo de Cesión de Prioridad Voluntaria (yield)
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();
}
Creación y Control de Thread en Java 26
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 27
© Antonio Tomeu
CLASE Thread: API DE CONTROL DE PRIORIDAD
package java.lang;
public
public
public
public
public
public
© Antonio Tomeu
PLANIFICACIÓN BASADA EN PRIORIDADES
„
class Thread implements Runnable
final static int Thread.MIN_PRIORITY;
final static int Thread.NORM_PRIORITY;
final static int Thread.MAX_PRIORITY;
void setPriority(int prioridad);
int getPriority():
Creación y Control de Thread en Java 29
Creación y Control de Thread en Java 28
„
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.
© Antonio Tomeu
Creación y Control de Thread en Java 30
5
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[]
{
new Prioridades(10).start();
new Prioridades(20).start();
new Prioridades(30).start();
new Prioridades(40).start();
new Prioridades(50).start();
new Prioridades(60).start();
}
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;
}
args)
//orden lanzamiento no es igual al orden
//de ejecución… pero
//¿ajustando las prioridades?
}
© Antonio Tomeu
Creación y Control de Thread en Java 31
}
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");
}
32
public class ThreadTestNuevaPrioridad {
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];
public static void main(String[] args) {
int nHilos = Integer.parseInt(args[0]);
long n = Long.parseLong(args[1]);
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) {}
}
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 33
© Antonio Tomeu
EJERCICIOS
„
„
„
„
„
„
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?
© Antonio Tomeu
Creación y Control de Thread en Java 35
Creación y Control de Thread en Java 34
MAPPING HILOS JVM-HILOS NATIVOS DE WIN32
„ 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
© Antonio Tomeu
Creación y Control de Thread en Java 36
6
MAPPING PRIORIDADES JVM A WIN32
MAPPING HILOS JVM-HILOS NATIVOS DE WIN32
„ Pero
Prioridad Java
Prioridad Win32
0
THREAD.PRIORITY_IDLE
1 (Thread.MIN_PRIORITY)
THREAD.PRIORITY_LOWEST
2
THREAD.PRIORITY_LOWEST
3
THREAD.PRIORITY_BELOW_NORMAL
4
THREAD.PRIORITY_BELOW_NORMAL
5 (Thread.NORM_PRIORITY)
THREAD.PRIORITY_NORMAL
6
THREAD.PRIORITY_ABOVE_NORMAL
7
THREAD.PRIORITY_ABOVE_NORMAL
8
THREAD.PRIORITY_HIGHEST
9
THREAD.PRIORITY_HIGHEST
10 (Thread.MAX_PRIORITY)
THREAD.PRIORITY_TIME_CRITICAL
© Antonio Tomeu
Creación y Control de Thread en Java 37
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.
© Antonio Tomeu
MAPPING PRIORIDADES JVM A PRIORIDADES
DE HILOS LINUX
MAPPING HILOS JVM-HILOS NATIVOS DE LINUX
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
„ Núcleos
© Antonio Tomeu
Creación y Control de Thread en Java 39
Prioridad Java
0
4
3
3
2
4
1
5 (Thread.NORM_PRIORITY)
0
6
-1
7
-2
8
-3
9
-4
10 (Thread.MAX_PRIORITY)
-5
© Antonio Tomeu
como root (o con privilegios
equivalentes, setuid)
„
„
„
Creación y Control de Thread en Java 41
Creación y Control de Thread en Java 40
CONTROL DE PRIORIDAD: CONCLUSIONES
„ Solo
© Antonio Tomeu
no contemplado
2
„
a la JVM indicando que
tenga en cuenta las prioridades.
Prioridad Linux (nice value)
1 (Thread.MIN_PRIORITY)
RESTRICCIONES DEL MAPPING PRIORIDADES JVM A LINUX
„ Parametrizar
Creación y Control de Thread en Java 38
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
© Antonio Tomeu
Creación y Control de Thread en Java 42
7
En el Próximo Tema…
„ Modelos
Teóricos de Control de la
Concurrencia
„ Variables Comunes
„ Semáforos
„ Regiones Críticas
„ Monitores
© Antonio Tomeu
Creación y Control de Thread en Java 43
8
Descargar