Anda di halaman 1dari 23

Programacin Concurrente con Java

Diseo de Sistemas Operativos Facultad de Informtica


Juan Pavn Mestras Dep. Sistemas Informticos y Programacin Universidad Complutense Madrid

Concurrencia
n

En el mundo real, muchas cosas pasan a la vez


n n

Con varias computadoras se pueden ejecutar varios programas a la vez Con una sola computadora se puede simular la ejecucin paralela de varias actividades:
? mltiples flujos de ejecucin (multithreading)

comparten el uso de un procesador


n

Java soporta la ejecucin paralela de varios threads (hilos de ejecucin)


n n

Los threads en una misma mquina virtual comparten recursos


por ejemplo, memoria

Los threads en varias mquinas virtuales necesitan de mecanismos de comunicacin para compartir informacin

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

Concurrencia
n

Para qu:
n n n n n

Mejorar la disponibilidad y eficiencia Modelar tareas, objetos autnomos, animacin Paralelismo: mltiples procesadores, simultanear E/S Proteccin: aislar actividades en hilos de ejecucin Ejemplos:
Tareas con mucha E/S: acceso a sitios web, bases de datos Interfaces grficas de usuario: gestin de eventos Demonios con mltiples peticiones de servicio simultneas Simulacin

Con cuidado:
n n

Complejidad: seguridad, viveza, composicin Sobrecarga: Mayor uso de recursos

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

Programacin concurrente OO
n

La concurrencia es natural en orientacin a objetos


n

Ya Simula67 soportaba concurrencia Programacin OO secuencial


Tiene ms importancia la seguridad y la viveza Pero tambin usa y extiende patrones de diseo comunes

Diferencias con otros modelos de concurrencia


n

Programacin orientada a eventos


Permite que pueda haber mltiples eventos a la vez Pero usa y extiende estrategias de mensajera

Programacin multithread de sistemas


Aade encapsulacin y modularidad Pero usa y extiende implementaciones eficientes

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

Programacin concurrente OO
n

Concurrencia y reusabilidad
n

Mayor complejidad
Criterios de correccin ms duros que con cdigo secuencial El no determinismo impide la depuracin y el entendimiento del cdigo

Mayor dependencia del contexto


Los componentes son seguros y vivos slo en determinados contextos: necesidad de documentacin Puede ser difcil extender mediante herencia (anomalas) Puede ser difcil la composicin: conflictos entre tcnicas de control de la concurrencia

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

Programacin concurrente OO
n

Polticas de diseo
n

Ejemplos
Dependencia de estado: qu hacer cuando se recibe una peticin que no se puede realizar Disponibilidad de servicio: Restricciones en el acceso concurrente a los mtodos Restricciones de flujos: Establecer direccionabilidad y reglas de capas para los mensajes

Combaten la complejidad
Reglas de diseo de alto nivel y restricciones arquitecturales evitan decisiones caso por caso inconsistentes

Mantener la apertura
Acomodar componentes que obedecen polticas determinadas

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

Modelos de objetos
n

4 operaciones bsicas
n n n n

Aceptar un mensaje Actualizar el estado local Enviar un mensaje Crear un nuevo objeto

Dependiendo de las reglas para estas operaciones, hay 2 categoras de modelos:


n n

Objetos activos Objetos pasivos

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

Modelo de objetos pasivos


En programas secuenciales, slo un objeto Programa es activo Los objetos pasivos encapsulan datos del programa

main

E/S

programa

Objeto Objeto Objeto pasivo pasivo Objeto pasivoObjeto pasivo Objeto pasivo pasivo
Juan Pavn Mestras, UCM 2001 Programacin concurrente con Java 8

Modelo de objetos activos


Cada objeto tiene su propio hilo de ejecucin (slo puede hacer una cosa a la vez)

mensaje

accion { actualiza estado envia mensaje crea objeto }

oneway

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

Sistemas = Objetos + Actividades


n

Objetos
n

n n

TADs, componentes agregados, monitores, objetos de negocio (EJBs), servlets, objetos CORBA remotos Se pueden agrupar de acuerdo a estructura, role, ... Se pueden usar para mltiples actividades
Lo ms importante es la SEGURIDAD

Actividades
n

n n

Mensajes, cadenas de llamadas, workflows, hilos de ejecucin, sesiones, escenarios, scripts, casos de uso, transacciones, flujos de datos Se pueden agrupar por origen, funcin, ... Comprenden mltiples objetos
Lo ms importante es la VIVEZA

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

10

Java
n

Soporte para ingeniera de software


n n n n n n

Empaquetado: objetos, clases, componentes, paquetes Portabilidad: bytecode, unicode, transportes varios Extensibilidad: Subclases, interfaces, class loader Seguridad: mquina virtual, verificadores de cdigo Bibliotecas: paquetes java.* Ubicuidad: corre en casi todas partes Concurrencia: threads, locks Distribucin: RMI, CORBA Persistencia: Serializacin, JDBC Seguridad: gestores de seguridad, dominios

Retos en nuevos aspectos de la programacin


n n n n

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

11

Concurrencia en Java
n

El cdigo que ejecuta un thread se define en clases que implementan la interfaz Runnable
public interface Runnable { public void run(); }

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

12

Concurrencia en Java
n

Clase Thread
n n

Un hilo de ejecucin en un programa Mtodos


run() actividad del thread start() activa run() y vuelve al llamante join() espera por la terminacin (timeout opcional) interrupt() sale de un wait, sleep o join isInterrupted() yield() stop(), suspend(), resume() (deprecated)

Mtodos estticos
sleep(milisegundos) currentTread()

Mtodos de la clase Object que controlan la suspensin de threads


wait(), wait(milisegundos), notify(), notifyAll()
Programacin concurrente con Java 13

Juan Pavn Mestras, UCM 2001

Concurrencia en Java
n

Creacin de Threads:
class MiThread extends Thread {...} => new MiThread() class MiThread implements Runnable {...} => new Thread(new MiThread())

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

14

Concurrencia en Java
import java.lang.Math ; ; import java.lang.Math class EjemploThread extends Thread {{ class EjemploThread extends Thread int numero; int numero; EjemploThread (int n) {{numero = n; }} EjemploThread (int n) numero = n; public void run() {{ public void run() try {{ while (true) {{ try while (true) System.out.println (numero); System.out.println (numero); sleep((long)(1000*Math.random())); sleep((long)(1000*Math.random())); }} }}catch (InterruptedException e) {{return; }}////acaba este thread catch (InterruptedException e) return; acaba este thread }} public static void main (String args[]) {{ public static void main (String args[]) for (int i=0; i<10; i++) for (int i=0; i<10; i++) new EjemploThread(i).start(); new EjemploThread(i).start(); }}

SALIDA
1 4 5 6 2 9 8 4 3 0 7 3 7 2 8 3 1 9 ...

}}

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

15

Sincronizacin en Java
n n

Necesaria para evitar colisiones entre hilos de ejecucin


n

Por ejemplo, accesos a memoria o a un recurso a la vez synchronized mtodo (...) {...} // a nivel de objeto synchronized (objeto) { ... } // a nivel de bloque de cdigo wait() y wait(timeout) El thread se queda bloqueado hasta que algn otro le mande una seal (notify) y entonces pasa a la cola de listos para ejecutar notify() y notifyAll()
? No es fcil la correcta programacin de la concurrencia y la sincronizacin

Sincronizacin:
n n

Mtodos
n

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

16

Sincronizacin en Java
public class NoSincronizada extends Thread { static int n = 1; public void run() { for (int i = 0; i < 10; i++) { System.out.println(n); n++; } } public static void main(String args[]) { Thread thr1 = new NoSincronizada(); Thread thr2 = new NoSincronizada(); thr1.start(); thr2.start(); } }
Juan Pavn Mestras, UCM 2001 Programacin concurrente con Java

1 2 3 4 5 6

Posible resultado de ejecucin

7 8 8 9 10 11 12 14 15 16 17 18 19 20

17

Ejercicio
n

Adaptar la clase anterior para que el resultado de la ejecucin de los dos threads en paralelo sea la secuencia de 1 a 20 sin repeticiones ni saltos de nmeros.

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

18

Monitores en Java
n

Un monitor es un objeto que implementa el acceso en exclusin mutua a sus mtodos


n

En Java se aplica a los mtodos de la clase declarados como synchronized


Los dems mtodos pueden accederse concurrentemente independientemente de si algn thread accede a ellos o a un mtodo synchronized

// ...dentro de cdigo synchronized, ya que el hilo debe ser el propietario del monitor // del objeto. Liberar el monitor hasta que lo despierten con notify o notifyAll, y // pueda retomar control del monitor try { wait(); // o wait(0); } catch (InterruptedException e) { System.out.println(Interrumpido durante el wait"); } // ...tambin dentro de cdigo synchronized notify(); // o notifyAll();
Juan Pavn Mestras, UCM 2001 Programacin concurrente con Java 19

Monitores en Java
n

Disciplinas de sealizacin de monitores


n

Signal and exit : Despus de hacer notify el thread debe salir del monitor y el thread que recibe la seal es el siguiente en entrar en el monitor Signal and continue: El thread que recibe la seal no tiene que ser necesariamente el siguiente en entrar en el monitor ?Puede haber intromisin: Antes que el thread despertado
podra entrar un thread que estuviera bloqueado en la llamada a un mtodo synchronized

En Java:
n n n

Los monitores siguen la disciplina signal and continue Tampoco se garantiza que el thread que ms tiempo lleve esperando sea el que reciba la seal con un notify() Cuando se usa notifyAll() para pasar todos los threads en wait a la cola de listos para ejecutar, el primer thread en entrar en el monitor no ser necesariamente el que ms tiempo lleve esperando
Programacin concurrente con Java 20

Juan Pavn Mestras, UCM 2001

10

Monitores en Java
n

Ejercicio Realizar un programa para determinar qu disciplina siguen los monitores Java
n

Una posible manera de hacerlo es creando varios threads que llaman a un mtodo de un objeto y dentro del mtodo hacen un wait(). Otro thread ser el encargado de despertarlos con notify() llamando a otro mtodo del mismo objeto Habr que sacar trazas en cada mtodo de los momentos en que un thread intenta entrar en el monitor, al hacer wait(), al despertarse, etc. Para lograr un mayor entrelazado de los threads para este experimento sera conveniente una instruccin sleep() en algunos momentos, por ejemplo antes y despus del wait() y del notify(). As el planificador podr dar entrada a otros threads.
Programacin concurrente con Java 21

Juan Pavn Mestras, UCM 2001

Propiedades de los programas concurrentes


n

En programacin secuencial:
Correccin parcial
correctamente) (Si el programa termina, realiza su funcin

+ Terminacin (el programa termina alguna vez) = Correccin total


n

En programacin concurrente:
n n n

Propiedades de seguridad
correctamente)

(Si el sistema evoluciona, lo hace

Propiedades de viveza (Si algo debe ocurrir, alguna vez ocurre) Equidad (Todo proceso que evoluciona lo hace recibiendo un trato
equitativo de los recursos)

Para verificar estas propiedades en un programa Java hay que entender cmo funciona la MVJ (y tambin para programar mejor...)
Programacin concurrente con Java 22

Juan Pavn Mestras, UCM 2001

11

Threads y Locks en la MVJ


Conceptos para la definicin del funcionamiento de la MVJ
n

Variables son los lugares en los que un programa puede almacenar algo
n n

Variables de clases (static) y de objetos, y tambin componentes de arrays Las variables se guardan en una memoria principal que es compartida por todos los threads El thread trabaja con copia de las variables en la memoria de trabajo La memoria principal tiene una copia maestra de cada variable

Cada thread tiene su propia memoria de trabajo


n n

La memoria principal puede contener tambin cerrojos (locks)


n n

Todo objeto Java tiene asociado un lock Los threads pueden competir para adquirir un lock
Programacin concurrente con Java 23

Juan Pavn Mestras, UCM 2001

Threads y Locks en la MVJ


Operaciones atmicas en la MVJ
n

Ejecutables por threads:


n n n n

use

variable)

(siempre que ejecute una instruccin de la MVJ que usa el valor de la (siempre que ejecute una instruccin de la MVJ de asignacin a la variable) (para poner en la copia de la variable el valor transmitido desde memoria (para transmitir a memoria principal el valor de la copia de la variable)

assign load store read


de

principal)

Ejecutables por la memoria principal


n

(para transmitir el contenido de la copia maestra de la variable a la memoria trabajo del thread) (para poner en la copia maestra de la variable el valor transmitido desde de trabajo del thread)

write

memoria

Ejecutables en sincronizacin por un thread y la memoria principal


n n

lock unlock

(para adquirir un claim en un cerrojo particular) (para liberar un derecho sobre un cerrojo particular)

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

24

12

Acceso a variables en la MVJ

Thread
Memoria Principal
Memoria de trabajo

read copia maestra

x x

load store assign

x
use

write

x
Mquina de ejecucin

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

25

Threads y Locks en la MVJ


n

Reglas de orden de ejecucin


n n n n

Las acciones realizadas por cada thread estn totalmente ordenadas Las acciones realizadas por la memoria principal sobre cualquier variable estn totalmente ordenadas Las acciones realizadas por la memoria principal sobre cualquier cerrojo estn totalmente ordenadas No se permite que ninguna accin se siga a s misma

Relacin entre las acciones de un thread y memoria principal


n n n

Toda accin lock y unlock se realiza a la vez por un thread y la memoria principal Toda accin load sigue a una accin read Toda accin write sigue a una accin store
Programacin concurrente con Java 26

Juan Pavn Mestras, UCM 2001

13

Threads y Locks en la MVJ


n

Variables long y double (no declaradas como volatile)


n

Se consideran en la MVJ como 2 variables de 32 bits cada una Son necesarias dos operaciones load, store, read o write para tratarlas Para soportar procesadores de 32 bits Aunque se admite que haya implementaciones de la MVJ con operaciones de 64bits atmicas (y actualmente se recomienda incluso)

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

27

Threads y Locks en la MVJ


n

Reglas sobre cerrojos


n n n

Un cerrojo slo puede pertenecer a un thread en un momento dado El cerrojo slo quedar libre cuando el thread haga tantos unlock como lock hubiera hecho Un thread no puede hacer unlock de un cerrojo que no poseyera Antes de hacer una operacin unlock un thread debe copiar en memoria principal todas las variables a las que hubiera asignado Despus de hacer lock un thread debe recargar de memoria principal todas las variables que utilice

Reglas sobre cerrojos y variables


n

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

28

14

Threads y Locks en la MVJ


t h r e a d uno memoria principal thread dos

public class Ejemplo { int x = 1, y = 2; void uno() { x = y; } void dos() { y = x; } }


que x acabe valiendo lo que y o que y acabe valiendo lo que x o que se cambien los valores de x e y
Juan Pavn Mestras, UCM 2001

read y load y use y assign x


[ store x]

read x load x use x assign y


[ store y]

[ write x ] [ write y ]

En este ejemplo en memoria principal puede acabar ocurriendo:

Programacin concurrente con Java

29

Threads y Locks en la MVJ


public class EjemploSincronizado { int x = 1, y = 2; synchronized void uno() { x = y; } synchronized void dos() { y = x; } }
A l u s a r l a o p e r a c i n unlock s e o b l i g a a e s c r i b i r e n m e m o r i a p r i n c i p a l , y p o r h a b e r u t i l i z a d o lock h a y m e n o s c o m b i n a c i o n e s , a s q u e b i e n : o x acaba valiendo lo que y o y acaba valiendo lo que x y no p u e d e h a b e r i n t e r c a m b i o d e v a l o r e s
Juan Pavn Mestras, UCM 2001 Programacin concurrente con Java 30

15

Threads y Locks en la MVJ


t h r e a d uno memoria principal thread dos lock EjemploSincronizado

lock EjemploSincronizado load y use y assign x


[ store x]

read y

read x

load x use x assign y


[ store y] [ write x ] [ write y ]

unlock EjemploSincronizado

unlock EjemploSincronizado

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

31

Threads y Locks en la MVJ


n

Ejercicios Escribir un programa que cree dos threads y pruebe la ejecucin de las clases de los ejemplos anteriores Considerar la clase Ejercicio2 a continuacin. Qu ocurre si un thread llama a uno() mientras otro llama a dos()? Qu ocurre si los mtodos son synchronized?
class Ejercicio2 { int a=1, b=2; void uno() { a=3; b=4; } void dos() { System.out.println(a = + a + , b= + b); }

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

32

16

Requisitos en programacin concurrente OO


n n n n

Seguridad: requisitos de integridad Viveza: requisitos de progreso Eficiencia: requisitos de efectividad Reusabilidad: requisitos composicionales

Polticas y protocolos reglas de diseo a lo largo del sistema

Estructuras de objetos patrones de diseo microarquitectura

Tcnicas de codificacin idioms trucos

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

33

Patrones de programacin concurrente


n

Patrones de diseo de seguridad


n n

Propiedades de seguridad: Nada malo ocurrir Los patrones tratan bsicamente de evitar que el estado del sistema se haga inconsistente Propiedades de seguridad: Nada malo ocurrir Los patrones tratan bsicamente de evitar que el estado del sistema se haga inconsistente

Patrones de diseo de viveza


n n

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

34

17

Patrones de diseo de seguridad


n

Objetos seguros: realizar acciones slo cuando se est en un estado consistente


n n

Conflictos lectura/escritura Fallos en invariantes

Estrategias para mantener la consistencia de estado en los objetos accedidos concurrentemente:


n n n n n

Inmutabilidad: Evitar los cambios de estado Sincronizacin mediante cerrojos: Asegurar dinmicamente un acceso exclusivo Contenimiento: Asegurar estructuralmente un acceso exclusivo ocultando objetos internos Dependencia de estado: qu hacer cuando no se puede hacer nada Particin: separar los aspectos independientes de objetos y cerrojos
Programacin concurrente con Java 35

Juan Pavn Mestras, UCM 2001

Patrones de diseo de seguridad


n

Inmutabilidad
n

Los objetos no se modifican, se crean


Vale para objetos que proporcionan servicios sin estado Ejemplos: clases String, Integer y Color de Java Otro ejemplo:

public class Punto { private int x, y; // coordenadas fijas public Punto(int x, int y) { this.x = x; this.y = y; } // otros mtodos que no cambian los valores de las coordenadas public x() { return x; } public y() { return y; } }
Juan Pavn Mestras, UCM 2001 Programacin concurrente con Java 36

18

Patrones de diseo de seguridad


n

Objetos sincronizados (cerrojos)


n

Cuando el estado de los objetos puede cambiar, es necesario sincronizar el acceso a los mtodos que pueden leer o modificar sus atributos Bsicamente puede haber dos tipos de conflictos al acceder a una variable:
Conflictos de lectura-escritura Conflictos de escritura-escritura

Uso de cerrojos:
Adquirir el objeto cerrojo al entrar en el mtodo, devolverlo al regresar Garantiza la atomicidad de los mtodos Riesgo de interbloqueos

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

37

Patrones de diseo de seguridad


n

Objetos sincronizados (cerrojos)


n

En general basta con declarar todos los mtodos como synchronized


Cada mtodo debe ser corto de ejecucin y asegurar que siempre acaba (para garantizar la viveza) En algunos casos no es necesario declarar el mtodo synchronized: Mtodos de acceso a valores de atributos de tipos sencillos Explotar la inmutabilidad parcial Arreglar la concurrencia para cada mtodo

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

38

19

Patrones de diseo de viveza


n n

Cada actividad debe progresar


n

cada mtodo llamado tendr que ejecutarse alguna vez Contencin: un thread no deja el procesador Reposo indefinido: un thread bloqueado nunca pasa a listo
Tras suspend nadie hace resume Tras wait nadie hace notify

Problemas de viveza:
n n

n n

Interbloqueo entre threads que se bloquean uno a otro Terminacin prematura: un thread muere antes de lo debido (p.ej. con stop) Cada mtodo llamada debera ejecutarse lo antes posible
Programacin concurrente con Java 39

Eficiencia
n

Juan Pavn Mestras, UCM 2001

Patrones de diseo de viveza


n

Balance seguridad-viveza: estrategias de diseo


n

Primero la seguridad:
Asegurar que cada clase es segura (todos los mtodos como synchronized) y entonces intentar mejorar la viveza para mejorar la eficiencia Anlisis de mtodos de acceso al monitor Particin de la sincronizacin Riesgo: Puede resultar en cdigo lento o propenso a interbloqueos

Primero la viveza:
Al principio no se tienen polticas de sincronizacin, y se aaden despus con compuestos, subclases, cerrojos, etc. Riesgo: puede resultar en cdigo con errores de condiciones de carrera

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

40

20

Acciones dependientes del estado


n

El estado de un objeto puede tener dos tipos de condiciones de activacin:


n

Internas: el objeto est en un estado apropiado para realizar una accin Externas: el objeto recibe un mensaje de otro pidindole que realice una accin

Precondiciones

accin

Postcondiciones

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

41

Acciones dependientes del estado


n

Estrategia general:
n

Para cada condicin que necesite esperarse, escribir un bucle guardado con un wait() Asegurar que todo mtodo que cambie el estado que afecte a las condiciones guardadas invoque notifyAll() para despertar cualquier thread que estuviera esperando un cambio de estado
// ... condicion = true; n o t i f y A l l ();

while ( ! condicion ) { t r y { w a i t (); } catch (InterruptedException e) { //... } }

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

42

21

Acciones dependientes del estado


Ejemplo clsico: semforo
public class Semaforo { private int cuenta; Semaforo(int cuentaInicial) { cuenta = cuentaInicial; } public synchronized void P() { while ( cuenta <= 0 ) try { wait(); } catch(InterruptedException e) {} cuenta--; } public synchronized void V() { cuenta++; notify(); }

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

43

Acciones dependientes del estado


n

Polticas para tratar pre- y post-condiciones


n n n n n n n n

Acciones ciegas: proceder en cualquier caso, sin garantas sobre el resultado Inaccin: ignorar la peticin si no se est en estado correcto Expulsin: Enviar una excepcin si no se est en el estado correcto Guardas: suspender hasta que se est en estado correcto Intento: proceder, ver si hubo xito, y si no, rollback Reintento: seguir intentando hasta tener xito Temporizacin: esperar o reintentar durante un tiempo, luego fallar Planificacin: iniciar primero una actividad que nos lleve a un estado correcto
Programacin concurrente con Java 44

Juan Pavn Mestras, UCM 2001

22

Prctica
n

Realizar un juego que pueda ejecutarse como applet


n n

El applet debe implementar la interfaz Runnable Cada entidad del juego puede ser controlada por un hilo de ejecucin separado:
Para la interfaz con de usuario Jugadores Supervisin del juego etc.

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

45

Bibliografa
n n

Java Virtual Machine Specification, 2nd edition D. Lea, Programacin concurrente en Java. Principios y Patrones de diseo (segunda edicin). Addison-Wesley 2000 S. Hartley, Concurrent Programming with Java

Juan Pavn Mestras, UCM 2001

Programacin concurrente con Java

46

23

Anda mungkin juga menyukai