Vista previa del material en texto
CLASE 12
MODELO DE DESARROLLO DE PROGRAMAS Y PROGRAMACION
CONCURRENTE – 2.019
FAC.DE INGENIERIA - UNJu
CONCURRENCIA EN JAVA USANDO HILOS
RAZONES PARA LA CONCURRENCIA EN JAVA USANDO HILOS
• Esquema Tradicional: 1 Comput. + 1 único procesador 1 sola instrucción
• Técnicas p/simular multiejecución en un instante de tiempo Sist.Operat.
multitarea (los programas del SO parecen ejecutarse simultáneamente
aunq esto no es posible, simulan multitarea)
• Existen procesadores de + de 1 núcleo (2, 4, 6, 8, 10, 12, .., 32 XEON), pero
estos pueden no ser suficientes p/la cant.de programas q desean hacer
uso de los mismos, es necesario plantear otra solución SW un programa
requiera la ejecución de varias tareas, HILOS O THREADS
• El uso de la multitarea como el de los multihilos implica una serie de
problemas como el acceso a ciertas instrucciones o “región crítica”
PROGRAMAS Y PROCESOS
• Un PROCESO es un programa en ejecución, es
decir el archivo .class después de haber
pulsado el botón Ejecutar de Eclipse. Es
DINAMICO (se está ejecutando, requiere
tiempo de CPU y almacenar su código en
memoria).
• Un PROGRAMA en JAVA es un
archivo .class, es ESTATICO (no
requiere espacio de memoria o
tiempo de CPU)
HILOS
• Es 1parte de 1proceso, q tiene variables locales propias y comparte la
memoria con el resto de hilos del mismo proceso. Los hilos permiten a
1aplicación realizar varias tareas a la vez (concurrentemente).
• La programación de aplicaciones q utilizan hilos no suele ser trivial, ya q se
debe controlar q el trabajo de 1 hilo no interfiera con el trabajo d otro hilo en
1 mismo proceso y también es necesario q los hilos se coordinen entre ellos.
• Los multihilos se ejecutan en lugar de multiprocesos x la rapidez d ejecución.
FORMAS DE IMPLEMENTAR UN HILO EN JAVA
• En el leng.Java hay 3 formas de implementar la concurrencia:
• 1) Herencia de la clase Thread:
public class hiloHerencia extends Thread{
public static void main(String[] args){
hiloHerencia hilo = new hiloHerencia();
hilo.setName("un hilo que hereda de Thread. ");
hilo.start();
}
public void run(){
System.out.println("Soy "+getName()+"Hola mundo!!!");
}
}
FORMAS DE IMPLEMENTAR UN HILO EN JAVA
• 2) Implementando interfaz Runnable:
Es conveniente implementar la interfaz Runnable cuando:
• Se necesita lanzar hilos sueltos o
• Si se quiere dotar de concurrencia a 1 clase que ya es base de 1 jerarquía y q
no podría heredar por lo tanto de Thread ya que en Java no existe la herencia
múltiple.
• 3) O utilizar objetos de la clase ThreadPoolExecutor
public class hiloRunnable implements Runnable{
public static void main(String[] args){
hiloRunnable run = new hiloRunnable();
Thread hilo = new Thread(run);
hilo.start();
}
@Override
public void run(){
System.out.println("Soy "+this.toString()+". Hola
mundo!!!");
} }
ANÁLISIS DE UN EJEMPLO USANDO HERENCIA DE LA CLASE THREAD
• Clase UnHilo: contiene el código q ejecuta un hilo
• Clase TestUnHilo: crea 1 instancia de la clase UnHilo y la ejecuta.
• La clase Thread tiene 1conj. d mét.q facilitan el trabajo con hilos. En ella se crea:
• Un constructor con 1parámetro de entrada tipo String (contiene el nombre del
hilo). El mismo llama al constructor de la clase base, enviando el parámetro de
entrada con el nombre.
• Un método run() q contiene el código con el trabajo que debe realizar el
thread. Este es el método más importante. En este ej. únicamente imprime por
pantalla el nombre del hilo.
public class UnHilo extends Thread {
public UnHilo(String nombreHilo) {
super(nombreHilo);
}
public void run() {
System.out.println(getName());
} }
public class TestUnHilo {
public static void main(String[] args) {
UnHilo hiloUno = new UnHilo("HiloUno");
hiloUno.start();
}
}
ANÁLISIS DE UN EJEMPLO USANDO HERENCIA DE LA CLASE THREAD
• P/q el hilo se ejecute, se incluye 1clase TestUnHilo q contiene el método
main(). En esta clase se observa q se creó una instancia del objeto UnHilo y
se llama a su método start(). El método start() no está definido en esta clase,
sino en la clase base Thread. Al utilizar este método, el objeto desde el que
se la llama (en nuestro caso, UnHilo) se pone en ejecución.
• El método start() hace una serie de procesos (que son transparentes para el
programador) e invoca al método run() del hilo.
• Terminación: Un hilo finaliza cuando culmina la ejecución de su método
run(). Estado MUERTO
HILOS: ESTADOS DE
UN HILO
Esquema con los
diferentes estados
(representados x
recuadros) en los q puede
encontrarse un hilo y q
métodos pueden llevar
ese estado al hilo
(representados x flechas).
Estados:
• Nuevo: Se ha creado un objeto hilo, pero todavía no se le ha asignado ninguna
tarea. P/ello, se ha de llamar a su método start() y el hilo pasará al estado preparado.
• Preparado: El hilo está preparado p/ejecutarse, pero el PLANIFICADOR DE HILOS es
quién debe decidir si puede hacerlo o debe esperar (por ejemplo, a que acabe la
ejecución otro hilo).
HILOS:
ESTADOS
DE UN
HILO
• En ejecución: 1 vez q el hilo puede acceder a tiempo de CPU, se ejecuta. Si el hilo
finaliza su trabajo completamente, pasará al estado muerto. Si el planificador de hilos
decide q ha cumplido su periodo de tiempo y el hilo no ha finalizado su trabajo,
pasará al estado preparado y esperará a q el planificador de hilos vuelva a darle
permiso p/ejecutarse.
• Bloqueado: El hilo no puede ejecutarse xq espera q ocurra algo. En cuanto ocurra
lo q lo está dejando bloqueado, pasará al estado preparado.
• Muerto: El hilo ha finalizado su tarea y deja de existir.
HERRAMIENTAS DE PLANIFICACIÓN DE HILOS
Java proporciona 1conj.de métodos q permiten controlar el cambio de 1hilo de un
estado a otro.
Método yield(): Un hilo puede ceder el tiempo de CPU q se le ha asignado p/q otros hilos
puedan ejecutarse. Con la llamada a este método, el hilo pasa del estado en ejecución
al estado preparado.
En el caso q no haya otro hilo esperando CPU, el planificador de hilos volverá a cambiar
el estado del hilo de preparado a en ejecución, p/q vuelva a ocupar tiempo de CPU.
Método sleep(parametroTiempo): El hilo pasa al estado bloqueado tantos milisegundos
como se le indiquen en el parámetro de entrada parametroTiempo. 1vez cumplido ese
tiempo, se pone en estado preparado.
Método join(): permite a un hilo quedar a la espera q termine un 2do.hilo. El método
join() suele utilizarse p/mantener 1orden en la secuencia de los hilos. Así, se puede
arrancar 1secuencia de hilos llamando a join() p/q c/u finalice en el orden q se ha
marcado según la llamada a join(). Es obligatorio controlar los métodos join() y sleep()
mediante la excepción InterruptedException. Ej.
HERRAMIENTAS DE PLANIFICACIÓN DE HILOS
Ej.
public class UnHilo extends Thread {
public UnHilo(String nombreHilo) {
super(nombreHilo);
}
public void run() {
System.out.println(getName());
}
}
public class TestUnHilo {
public static void main (String[] args) {
UnHilo hiloUno = new UnHilo("HiloUno");
UnHilo hiloDos = new UnHilo("HiloDos");
hiloUno.start();
hiloDos.start();
try {
hiloUno.join();
hiloDos.join();
} catch (InterruptedException ie) { }
System.out.println("El programa ha finalizado"); }
}
REGIÓN CRÍTICA EN UN PROGRAMA (EJ.)
public class HiloContador extends Thread {
private Contador contador;
public HiloContador(String nNombre, Contador nContador) {
super(nNombre);
contador=nContador; }
public void run() {
try { for (int j=0; j<10; j++) {
int i=contador.getContador();
sleep((int) (Math.random()*10));
contador.setContador(i+1);
System.out.println(getName() + " pone el contador a " + i); }
} catch (InterruptedException e) {
System.out.println("Error al ejecutar el método sleep"); } } }
public class Contador {
private int contador = 1;
public void setContador(int nContador)
{
contador = nContador; }
public int getContador() {
return contador; } }
La clase Contador contiene la región crítica, en este caso un
sencillocontador. Se introdujo en la clase un atributo de tipo
entero inicializado a 1 y su correspondiente setter y getter.
La clase HiloContador es el hilo q ejecuta la región crítica. Se trata
de la ejecución de 1bucle en 10 iteraciones en el q se incrementa
en 1 el valor de la variable contador. Así, finalmente, c/hilo habrá
aumentado en 10 unidades el valor inicial que tenía el contador.
Se introdujo un tiempo de espera con sleep() entre la recogida
del valor de contador (getContador()) y su asignación con su
incremento en 1 (setContador()) p/q la CPU no realice de 1sola
vez todo un hilo y después otro.
REGIÓN CRÍTICA EN UN PROGRAMA
public class TestHiloContador {
public static void main(String[] args) {
Contador cont = new Contador();
HiloContador hc1 = new HiloContador("HiloUno", cont);
HiloContador hc2 = new HiloContador("HiloDos",cont);
hc1.start();
hc2.start();
try{ hc1.join();
hc2.join();
} catch (InterruptedException e) {
System.out.println("Error al ejecutar el método join");
}
System.out.println("El último valor que debe mostrarse es 10*2=20");
} }
La clase TestHiloContador es la q contiene el método main() q crea un objeto
Contador y 2hilos HiloContador, q permite ver el funcionamiento del
programa con 2hilos ejecutándose simultáneamente.
REGIÓN CRÍTICA EN UN PROGRAMA
Al ejecutar se aprecia q el resultado no es el esperado y q varía c/vez q se ejecuta el
programa. Esto se debe a q existe 1región crítica (la variable contador) q no se controla.
1solución a este problema es utilizar synchronized(nombreDeVariableSeccionCritica) y
recoger todo el código de la región crítica dentro de 1bloque. Se debe modificar el
código de la región crítica, en este caso, el método run() del hilo:
public void run() {
try { synchronized (contador) {
for (int j = 0; j < 10; j++) {
int i = contador.getContador();
sleep((int) (Math.random() * 10));
contador.setContador(i + 1);
System.out.println(getName() + " pone el contador a " + i);
}
}
} catch (InterruptedException e) {
System.out.println("Error al ejecutar el método sleep");
}
} }
MULTIPROCESAMIENTO EN JAVA
El multiprocesamiento es la ejecución de varios procesos de manera concurrente o
al mismo tiempo. Con Java el multiprocesamiento p/aprovechar los núcleos de 1 o
varios procesadores viene gestionado con la máquina virtual de Java.
El programador sólo debe preocuparse xq el programa se ejecute x “a bloques”,
poner c/tarea en 1 hilo de ejecución y luego la máquina virtual s encarga d
gestionar todo, similar a lo q ocurre con los Sist.Operat. (SO), donde s tienen muchos
procesos q se ejecutan desde q inicia la computadora, y el SO se encarga de
gestionar q se ejecuta en c/momento. De igual manera en Java se puede gestionar
q se ejecuta o q se queda esperando, también se le puede decir a todas las tareas
q se ejecuten a la vez simplemente sin esperar a otros procesos. Ej. de creación de
2 tareas a y b:
MULTIPROCESAMIENTO EN JAVA
Ej. de creación de 2 tareas a y b:
// PROGRAMA PRINCIPAL ----
public class manejadorDeHilos {
public static void main(String[] args) {
unaTareaEnUnHilo a, b;
a = new unaTareaEnUnHilo();
b = new unaTareaEnUnHilo();
a.start();
b.start();
}
}
// FIN DEL PROGRAMA PRINCIPAL ----
// Éste es un objeto q representa
// 1 tarea q se ejecuta en 1 hilo
class unaTareaEnUnHilo extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print(i + ", ");
}
}
}
La clase manejadorDeHilos es 1objeto sencillo q crea 2variables a y b, las
dos del mismo tipo. La clase unaTareaEnUnHilo hereda de la clase Thread.
MULTIPROCESAMIENTO EN JAVA
Se debe hacer q las tareas en una clase q hereda de Thread se ejecuten en
1 hilo, y a la vez programar el método run(), q es el q se ejecuta cuando el
hilo arranca. Las variables del tipo unaTareaEnUnHilo tienen un método
propio de la clase Thread q es start(), con el cual se le dice q comience su
ejecución, el cual lo q hace es simplemente ejecutar la función run() como
en el ej.
Al ejecutarse se advierte que no se ha ejecutado la tarea a y luego la b y
que están desordenados los nros. Las 2 tareas se han ejecutado a la vez, y
cuando han podido han ido escribiendo el nro.x el q iban contando. Si se
ejecuta varias veces el programa se verá q da resultados distintos. Se podría
crear un vector de hilos y q el hilo 1 haga dormir al 2 y cuando haya
terminado de ejecutar su tarea q corra el 2. Y de paso un 3er.hilo q se
ejecute continuamente. Por lo tanto se debe modificar:
MULTIPROCESAMIENTO EN JAVA
class unaTareaEnUnHilo extends Thread {
public void run() {
for (int i = 0; i < 100000000; i++) {
for (int j = 0; j < 100000000; j++) {
for (int k = 0; k < 100000000; k++) {
for (int l = 0; l < 100000000; l++) {
}
}
}
}
}
}
Ahora va a tardar mucho c/hilo, y esta
vez se va a notar si se han usado los
hilos o no en el tiempo total q va a
tardar, aunq tal vez tarde demasiado y
haya q pararlo antes. Se puede
observar la ejecución con el visor de
los núcleos del PC. Al ejecutar el
programa con las modificaciones, el
administrador de tareas, muestra los
núcleos del donde se corrió, de los
cuales si son 4 el segundo y tercero
son los que están ejecutando las
tareas a y b. Y el procesador trabaja al
50% aproximadamente. Si se pusieran
2tareas más con hilos subiría al 100% y
se aprovecharía aún más el
procesador.
MULTIPROCESAMIENTO EN JAVA
Se puede modificar el ej.anterior cambiando sólo el manejadorDeHilos p/verlo al
100%:
public class manejadorDeHilos {
public static void main(String[] args) {
unaTareaEnUnHilo a, b, c, d;
a = new unaTareaEnUnHilo();
b = new unaTareaEnUnHilo();
c = new unaTareaEnUnHilo();
d = new unaTareaEnUnHilo();
a.start();
b.start();
c.start();
d.start();
}
}
Si no se hubieran utilizado los hilos,
p/ejecutar las 2 tareas, el procesador
habría trabajado al 25%
aproximadamente y tardado mucho
más en terminar. Se hubiera ejecutado
primero una, luego la otra, y los nros.
hubieran salido todos ordenados por la
pantalla. ¿Qué pasa entonces cuando
se tienen muchas tareas? Se debe
hacer un vector de tareas, una cola de
tareas o una lista. Mientras mas tareas
existan es mejor que estén
programadas c/u en un hilo.
• DNB – Ingeniería del Software. 2.012. Empezando con Concurrencia en
Java. Disponible en goo.gl/5M5yni. Accedido en Septiembre del 2.018
• Multiprocesamiento en Java. Disponible en goo.gl/aGTEfT. Accedido en
Septiembre del 2.018
• García de Jalón J, Rodríguez J, Mingo I, Imaz A, Brazalez A, Larzabal A,
Calleja J y García J. 2.000. Aprenda Java como si estuviera en primero.
• García Marcos, Cristian. 2.010. Concurrencia en Java. Disponible en
https://goo.gl/rb1HDg. Accedido en Septiembre del 2.018
BIBLIOGRAFÍA RECOMENDADA