Download public class Printer extends Thread

Document related concepts
no text concepts found
Transcript
TEMA 2.
Programación Concurrente
1.
2.
3.
4.
5.
6.
7.
Introducción
Primer ejemplo y estado de los threads
Grupos de Threads y planificación interna.
Concurrencia.
El problema del productor/consumidor
El problema de los lectores y escritores.
Aplicaciones.
1
Bibliografía
Bishop,J. Java. Fundamentos de
programación. Capítulo 13.
Bobadilla, J. y otros. Comunicaciones y
Bases de Datos en Java. Capítulo 2.
Doug Lea. Programación Concurrente en
Java.
2
1 . Introducción


La habilidad de un programa individual de
hacer más de una cosa a la vez es
implementado normalmente a través de lo
que se conoce como threads (hilos, hebras
o procesos java).
Una hebra puede definirse de forma intuitiva
como un trozo de ejecución del programa
que tienen lugar simultáneamente con, e
independientemente, de cualquier otra cosa
que se esté ejecutando.
3
1 . Introducción



Intuitivamente debemos distinguir
entre la multitarea de un sistema
operativo y la programación en
threads.
Los entornos que permiten el uso de
threads permiten la exclusión mutua
en recursos compartidos mediante la
sincronización.
Se debe tener mucho cuidado con el
abrazo mortal (deadlock).
4
1 . Introducción




La elección exacta de qué parte del sistema
debe ser sincronizada es la parte más
delicada de la programación concurrente.
La JVM no es un proceso ejecutable, es una
especificación. Un programa que
implementa la especificación es lo que se
conoce como java runtime (JRE).
Las aplicaciones Java y los applets son
threads por sí.
La forma en que se implementa threads en
Java es mediante la clase java.lang.Thread
que implementa la interface runnable.
5
2. Primer ejemplo
public class Printer extends Thread
{
public void run() {
for (int b = -128; b < 128; b++)
{
System.out.println(b);
}}}
6
2 . Primer ejemplo
public class Printer implements
Runnable {
public void run() {
for (int b = -128; b < 128; b++)
{
System.out.println(b);
}}}
7
2. Primer ejemplo

En el primer caso se crearía una instancia de la forma habitual
Printer p=new Printer();

y en el segundo:
Printer p=new Printer();
Thread proc =new Thread(p);


Aunque el objeto de tipo Runnable contiene la lógica
principal, la clase Thread es la única que encapsula el
mecanismo para lanzar y controlar el thread.
Para lanzar a ejecución un hilo debemos utilizar el método
start(), que invocará al método run().
8
2 . Primer ejemplo
Estados

Esquemáticamente, la vida de un
Thread se corresponde con el
siguiente gráfico:
NEW

DEAD
RUNNABL
E
NOT
RUNNABLE
9
2 . Primer ejemplo

Para cada uno de los casos:
public class ThreadTest {
public static void main(String args[]) {
Printer bp = new Printer();
bp.start();
for (int i = -128; i < 128; i++) {
System.out.println("Metodo main: " + i);
} }}
10
2 . Primer ejemplo

Para cada uno de los casos:
public class ThreadTest {
public static void main(String args[]) {
Printer r= new Printer();
Thread bp = new Thread(r);
bp.start();
for (int i = -128; i < 128; i++) {
System.out.println("Metodo main: " + i);
}}}
11
2 . Primer ejemplo

Es conveniente darle a cada thread un nombre distinto. Para
identificarlos se le suele pasar un String al constructor
(sino, el sistema lo nombra internamente como Thread-N) que
me sirva para identificarlo.
public class MultiThreadTest {
public static void main(String args[]) {
Printer bp1 = new Printer("bp1");
Printer bp2 = new Printer("bp2");
Printer bp3 = new Printer("bp3");
bp1.start();
bp2.start();
bp3.start();
}
}
12
2 . Primer ejemplo
public class Printer extends Thread {
public Printer(String s) {
super(s);
}
public void run() {
for (int b = -128; b < 128; b++) {
System.out.println(getName() + ": " + b);
}}}
13
3 . Planificación interna

A veces es conveniente darle más importancia a unos sobre otros
public class TestPrioridad {
public static void main(String args[]) {
Printer bp1 = new Printer("Pepe");
Printer bp2 = new Printer("Maria");
Printer bp3 = new Printer("Juan");
bp1.setPriority(Thread.MIN_PRIORITY);
bp2.setPriority(Thread.NORM_PRIORITY);
bp3.setPriority(Thread.MAX_PRIORITY);
bp1.start();
bp2.start();
bp3.start();
}}
14
3 . Planificación interna


La Java Virtual Machine utiliza un algoritmo
reentrante, pero no round-robin.
A veces interesa ralentizar un thread, dormirlo.
try {sleep(1000);}
catch (InterruptedException e) {}
15
4 . Concurrencia



Hay muchas veces que los threads deben de
comunicarse y sincronizarse.
La comunicación permite que la ejecución de
hilo influya en la ejecución de otro mediante el
intercambio de datos.
La sincronización permite establecer
restricciones sobre el orden de ejecución entre
diferentes hilos.
16
4 . Concurrencia


La sincronización limita el acceso a un método u objeto de un
thread cada vez. Para sincronizar los objetos o métodos se añade
la palabra reservada synchronized, después del modificador de
acceso.
De esta forma se accede en exclusión mutua al objeto o al método.
synchronized void metodo();


En Java se sigue el mismo mecanismo que describió Hoare, pero
con una variación: se sincronizan los métodos mediante una
palabra reservada, no mediante el uso explícito de un monitor.
Todo objeto en Java tiene asociado un monitor implícito.
17
4 . Concurrencia
public class Printer {
static int i = 0;
public synchronized void print() {
for (i = 1; i <= 10; i++) {
System.out.println(i);
}}}
18
4 . Concurrencia

Debemos tener alguna forma que bajo algunas condiciones nos
permita bloquear el Thread hasta que el recurso que necesite se
encuentre libre. La forma de hacer esto es mediante el uso de las
instrucciones wait() y notify().
try {
// ejecutar el código
wait();
} catch (InterruptedException e){
}


Tanto el wait() como el notify() se deben invocar desde métodos
synchronized, ya que sólo se pueden invocar si el thread en
cuestión posee el monitor del objeto, cosa que sólo ocurre si
accede a un método synchronized. Hay que tener en cuenta que al
hacer un wait() se liberan todos los recursos, incluyendo el
monitor.
En cuanto al notify(), puede ocurrir que cuando es invocado haya
en la cola cero o más threads esperando.
19
5 . Problema del productor consumidor
Almacen
class Almacen {
private int seq;
private boolean disponible = false;
public synchronized int consumir() {
while (disponible == false) {
try {
wait();
}
catch (InterruptedException e) {}
}
disponible = false;
notify();
return seq;
}
20
5 . Problema del productor consumidor
Almacen
public synchronized void producir(int value)
{
while (disponible == true) {
try {
wait();}
catch (InterruptedException e){}
}
seq = value;
disponible = true;
notify();
}
}
21
5 . Problema del productor consumidor
Productor
class Productor extends Thread {
private Almacen almacen;
private int numero;
public Productor(Almacen c, int numero) {
almacen = c;
this.numero = numero;}
public void run() {
for (int i = 0; i <= 10; i++) {
almacen.producir(i);
System.out.println("Productor #" + this.numero +
" pon: " + i);
try {
sleep((int)(Math.random() * 100));}
catch (InterruptedException e) {}}}}
22
5 . Problema del productor consumidor
Consumidor
class Consumidor extends Thread {
private Almacen almacen;
private int numero;
public Consumidor(Almacen c, int numero) {
almacen = c;
this.numero = numero;
}
public void run() {
int value = 0;
for (int i = 0; i <= 10; i++) {
value = almacen.consumir();
System.out.println("Consumidor #" +
this.numero + " cons: " +value);}}}
23
5 . Problema del productor consumidor
Principal
class ProdConTest {
public static void main(String args[]) {
Almacen c = new Almacen();
Productor p1 = new Productor(c, 1);
Consumidor c1 = new Consumidor(c, 1);
p1.start();
c1.start();
}
}
24
6 . Problema de lectores y escritores
Lectores/ Escritores
“El problema de los lectores y escritores
es una abstracción del acceso a una base
de datos, donde varios procesos pueden
leer pero la escritura debe de hacerse en
exclusión mutua.”
Su resolución esta en un fichero aparte.
25
7 . Aplicaciones
Aplicaciones





Servicios Web: la mayoría de los servicios basados en sockets. Demonios
de http (cliente y servidor). Motores de servlets. Servidores de
aplicaciones.
Cálculo Numérico: tratan de maximizar el rendimiento haciendo uso del
paralelismo.
Procesamiento de entrada/salida: los programas concurrentes pueden
utilizar los tiempos de espera perdidos en operaciones lentas de
entrada/salida y , por lo tanto hacer un uso más eficiente de los recursos
del sistema.
Simulación: estimación de productividad de un sistema y video juegos.
Aplicaciones basadas en IGU: la concurrencia permite el uso de controles
incluso en acciones que consumen mucho tiempo.
26