Programación Concurrente
Programación Concurrente
Programación Concurrente
Paralelismo: una computadora con mltiples procesadores puede ejecutar varios programas
simultneamente.
Proceso B
Proceso C
memoria
exclusiva y
Recursos
necesarios
memoria
exclusiva y
Recursos
necesarios
memoria
exclusiva y
Recursos
necesarios
Hilo B
Hilo C
Ventajas:
-permite un intercambio de CPU ms rpido.
-es menos costoso intercambiar 2 hilos que acceden a la misma memoria (slo se debe almacenar la pila
de llamadas y la informacin de contexto del hilo que sale del procesador), que intercambiar 2 procesos
con mapeos diferentes ( adems del cambio de contextos, se deben copiar las tablas de memoria virtual).
Desventajas:
-Al acceder todos los hilos a la misma memoria hay riesgos de sobrescribir informacin que est siendo
accedida por otro hilo.
Un programa en Java contiene un nico proceso que es la mquina virtual que puede ejecutar mltiples
hilos internamente.
Si ejecutamos un programa comenzaremos con unos 5 hilos:
El hilo principal, ejecuta el cdigo contenido en el main ().
El ciclo de eventos, recibe las notificaciones del sistema operativo e informa al componente de
la ocurrencia de un evento.
embargo, una instancia de Thread es un objeto hilo que permite crear y administrar un hilo de ejecucin,
existe desde antes del inicio del hilo hasta despus que muere.
Nota: por estar en el paquete java.lang, puede ser utilizada en cualquier lugar sin importar el
paquete. Principales mtodos que nos ofrece esta clase:
public Thread (): constructor sin parmetros que crea un objeto hilo.
public Thread (Runnable r): constructor que crea un objeto hilo que tendr como objetivo de
ejecucin al objeto recibido por parmetro.
public void start (): crea el hilo de ejecucin que va a estar asociado al objeto hilo
que recibi el mensaje, e inicia su ejecucin en el mtodo run().
public void run(): representa el cuerpo del hilo. Cuando este mtodo finalice, finalizar
la ejecucin del hilo.
public boolean isAlive (): indica si el hilo est vivo (desde que se llama a start () hasta que
finaliza run()).
public void setDaemon (boolean b): permite marcar a un hilo como demonio.
public static Thread currentThread (): retorna una referencia al objeto hilo que est
asociado al hilo de ejecucin que ejecut la sentencia. Es til cuando tenemos un pedazo de cdigo
que puede ser ejecutado por mltiples hilos y queremos saber quin lo ejecuta en cada momento.
public static void sleep (long milis): mtodo que manda a dormir a un hilo una cantidad
determinada de milisegundos, manteniendo todo su contexto. Una vez transcurrida la cantidad
indicada, el hilo pasa a estar disponible para que el scheduler le asigne tiempo de CPU. Este
mtodo es esttico porque el nico que puede afectar la ejecucin de un hilo es el hilo mismo. De
este modo, el mtodo sleep () afecta slo al hilo que ejecuta la llamada.
public static void yield(): hace que el hilo que lo ejecuta ceda el procesador para que otros
hilos se puedan ejecutar. Es un mtodo esttico porque slo el hilo que tiene el procesador es quien
lo puede ceder.
public void join (): hace que el hilo que pasa el mensaje espere a que el receptor del mensaje
finalice.
public void join (long milis): igual que el anterior, salvo que se espera hasta un mximo de
tiempo indicado por parmetro. Si transcurre un tiempo igual o mayor a la cantidad de
milisegundos especificados, el hilo que esperaba abandona la espera y contina ejecutndose.
public boolean isInterrupted (): indica si el hilo que recibe el mensaje ha sido interrumpido.
PRIORIDADES Y EL SCHEDULER
Cuando creamos un hilo, ste toma por defecto la prioridad del hilo desde el cual se lo cre. Antes de
llamar a start()) podemos asignarle una prioridad diferente utilizando setPriority (int p). Existen 3
constantes definidas en la clase Thread:
Thread.MIN_PRIORITY: valor entero (suele ser 1) que es la mnima prioridad que puede tener un
hilo.
Thread.MAX_PRIORITY: valor entero (suele ser 10) que es la mxima prioridad que puede tener un
hilo.
Las prioridades son una recomendacin que le damos al scheduler pero ste no est obligado a
considerarla.
Object
Thread
public void run()
MiHilo
public void run()
La clase Thread implementa la interfaz Runnable. Esta interfaz declara slo 1 mtodo: el mtodo run
().
Ejemplo: hilo que extiende la clase Thread
public class MiHilo extends Thread {
public void run () {
for (char c = a; c < a + 10; c++)
System.out.println(c);
}
}
public class Main {
public static void main (String args [ ] ) {
//se crea una instancia del hilo y se lo inicia
Thread hilo = new MiHilo();
hilo.start();
//se imprime la serie de nmeros simultneamente
For (int i = 0; i< 10; i++)
System.out.println (i);
}
}
IMPLEMENTANDO RUNNABLE
La interfaz java.lang.Runnable declara un solo mtodo (public void run ()) que deber estar
implementado para que los objetos de una clase puedan ser ejecutados.
Podemos crear una clase que implemente Runnable pero las instancias de esta clase no van a ser hilos.
Para poder crear un hilo necesitamos crear una instancia de Thread. Este ltimo recibir en su constructor
una instancia de MiRunnable . As, cuando se inicie el hilo se ejecutar el mtodo run () propio y ste
llamar al mtodo run () de la instancia de MiRunnable.
Implementando la interfaz Runnable
Runnable
Object
Thread
MiRunnable
public void run()
MiHilo
public void run()
El mtodo run () que viene implementado en la clase Thread se fija si se le ha pasado algn objetivo de
ejecucin. Si este no es nulo, se invoca al mtodo run () de ese objeto.
Ventajas de este modelo:
Las instancias de nuestra clase no son hilo, sino que contienen cdigo que puede ser ejecutado
dentro de un hilo. Esto es ms correcto que extender a Thread, ya que nuestras instancias slo van
a tener los tipos que le corresponden.
Nuestra clase contiene slo los mtodos y atributos que necesita para realizar su tarea.
Nuestra clase puede extender a cualquier otra clase, sin quedar atada a extender siempre a
Thread.
Hilo B
1
2
3
4
5
Thread.interrupted())
Al extender a Thread, nuestra clase tambin tiene todos los miembros de aqulla. Esto hace que
sus instancias sean ms grandes, ocupen ms memoria, y tengan muchos elementos que podemos
no necesitar.
Como Java slo soporta herencia simple, al extender a Thread no podremos derivar de otra clase
base.
Cuando se llama al mtodo interrupt(), el mtodo run de un hilo tendr que comprobar de vez en cuando si debe salir. Un
hilo no debe estar continuamente trabajando, sino que debe "echarse a dormir" de vez en cuando para permitir que otros
hilos tambin trabajen. Pero cuando est "durmiendo", no puede comprobar si debe finalizar. Aqu entra en accin
InterruptedException. La alternativa es:
-Ignorar la interrupcin atrapando la excepcin y continuando.
-Interpretar la interrupcin como una solicitud de terminacin.
Si el mtodo interrupt no encuentra al thread "durmiendo" o "esperando", la InterruptedException no se generar y el hilo
necesita llamar al mtodo interrupted para determinar si ha sido interrumpido recientemente.
Si un hilo fue bloqueado mientras esperaba la finalizacin de una operacin de entrada/salida, esas operaciones no
seran finalizadas por la llamada a interrupt. (A diferencia de sleep() o wait())
SINCRONIZACIN DE HILOS
Cuando hay hilos trabajando al mismo tiempo sobre un conjunto compartido de recursos y datos, debemos
sincronizar su tarea.
SECCIONES CRTICAS: bloque de cdigo que accede un recurso compartido que no pueden ser
accedidos simultneamente por ms de un hilo de ejecucin. Es habitualmente un rea en la que se
modifican variables asociadas, de modo que si un hilo se encuentra en el medio de dichas actualizaciones
y otro hilo hace uso de dichas variables el resultado obtenido no ser consistente.
EJEMPLO: el flujo de salida System.out: si enviamos una cadena de texto larga para que sea impresa por
este objeto, ningn otro hilo podr imprimir nada hasta que la impresin nuestra cadena no finalice.
ACCESO SECUENCIAL A SECCIONES CRTICAS: SYNCHRONIZED
Java nos provee una palabra reservada que podemos utilizar para bloquear objetos cuando accedamos a
secciones crticas. Esta palabra es synchronized, que puede ser utilizada como modificador de mtodos o
como sentencia, cumpliendo la misma funcin. Si algn hilo quiere hacer uso del objeto compartido sin
tomar el bloqueo, lo podr hacer aunque otro hilo de ejecucin tenga el objeto bloqueado.
El modificador synchronized puede ser aplicado slo a mtodos y se bloquear al objeto
implcito Esto tiene 2 consecuencias:
Los casos en los que se debe utilizar la sentencia en lugar del modificador synchronized son:
Cuando no es necesario mantener el bloqueo de un objeto durante todo un mtodo, dado que la
seccin crtica abarca slo una parte de este mtodo.
Cuando el objeto compartido pertenece a una clase cuyo cdigo fuente no controlamos y, por ende,
no podemos agregar el modificador synchronized a sus mtodos.
Los bloques synchronized se pueden anidar de modo de tomar todos los bloqueos necesarios para llevar
a cabo la tarea que el hilo necesite. Un hilo nunca se bloquea a s mismo, osea que si un hilo tiene
tomado el bloqueo de un objeto e invoca un mtodo synchronized sobre el mismo objeto, este mtodo se
puede ejecutar dado que ya posea el bloqueo necesario para ejecutarlo.
Cuando un hilo llama a un mtodo sincronizado, dicho mtodo terminar su cometido antes de que otro hilo pueda
ejecutar cualquier otro mtodo sincronizado del mismo objeto.
Se etiquetarn como synchronized aquellos mtodos que realicen mltiples operaciones de actualizacin en una
estructura de datos, as como aquellos que recuperen valores desde dichas estructuras.
public void notify (): este mensaje es pasado a un objeto compartido sobre el que hay algn
hilo esperando. Slo uno de los hilos que hayan estado esperando sale de la espera y puede
continuar su ejecucin.
public void notifyAll (): este mensaje es pasado a un objeto compartido sobre el que hay uno
o ms hilos esperando. Todos los hilos que estaban esperando salen de la espera y compiten por
obtener el bloqueo del objeto compartido para continuar su ejecucin.
Para invocar cualquiera de estos mtodos se debe tener tomado el bloqueo del objeto al que se le pase el
mensaje, para evitar que algn otro hilo modifique el estado del objeto compartido desde el momento en
que se define que se debe invocar el mtodo y se realiza la invocacin.
El mtodo wait() libera todos los bloqueos que el hilo tena tomados al momento de entrar en espera.
Cuando el hilo es notificado e intenta volver a ejecutarse lo primero que debe hacer es recuperar todos los
bloqueos que tena tomados antes de ir a la espera. Recin una vez que todos los bloqueos pudieron ser
recuperados, el hilo vuelve a estar disponible para que el scheduler le d tiempo de ejecutarse.
CICLO DE VIDA DE UN HILO
notify()
notifyAll()
start()
Creado
Ejecutable
Esperando
wait()
Durmiendo
Muerto
En ejecucin
sleep()
Bloqueado
Una vez que un hilo ha finalizado su ejecucin y muere no puede volver a ser ejecutado nuevamente, es
decir que no puede pasarse ms de 1 vez el mensaje start() a un hilo.
Los hilos pueden estar en uno de estos cuatro estados:
Nuevo: cuando se crea un thread con el operador new, dicho thread todava no est ejecutndose.
Ejecutables: Una vez que se ha llamado al mtodo start, el hilo est en condiciones de ejecutarse, lo cual no implica que
est ejecutndose. Es el sistema operativo el que debe dar ocasin de activarse. Java no distingue entre ejecutable y en
ejecucin, lo considera ejecutable.
Los sistemas operativos otorgan a cada hilo ejecutable una porcin de tiempo para realizar su tarea. Cuando dicha
porcin termina, el sistema operativo da el control a otro hilo.
Bloqueados: un hilo entra en estado bloqueado cuando se produce una de estas situaciones:
1. el hilo realiza una llamada al mtodo sleep() del thread.
2. el hilo llama a una operacin de entrada/salida
3. El hilo llama al mtodo wait().
4. El hilo intenta bloquear un objeto que ya se encuentra bloqueado por otro hilo..
5. Otro hilo ejecuta sobre el actual el mtodo suspend().
Cmo salir del estado bloqueado: Puede salir del estado bloqueado y regresar al de ejecutable siguiendo el camino
inverso al recorrido para llegar al estado bloqueado:
1. Si un hilo se ha puesto a "dormir", debe expirar el nmero de milisegundos especificado.
2. Si un hilo est esperando una operacin de entrada o de salida, sta operacin debe finalizar.
3. Si un hilo llam a wait, otro hilo debe invocar a notifyAll o notify.
4. Si est esperando por un objeto bloqueado que es propiedad de otro hilo, este ltimo debe liberar ese bloqueo.
5. Si un hilo ha sido suspendido, es necesario que se llame al mtodo resume. Sin embargo, y ya que dicho mtodo est
censurado, es mejor no usarlo en los programas.
Si se invoca a un mtodo de un hilo que es incompatible con su estado, la mquina virtual dispara una
IllegalThreadStateException.
Muertos: Un hilo muere por alguna de estas dos razones:
. Naturalmente, debida a una salida normal del mtodo run().
. Inesperadamente, una excepcin no capturada que finaliza el mtodo run().
Para determinar si un hilo est vivo (en estado ejecutable o bloqueado), se usa el mtodo isAlive, el cual devuelve true si
est en cualquiera de estos dos estados, o false si el thread es nuevo o si ha muerto.
Existen algunas limitaciones en la capacidad de Java para determinar exactamente el estado de un hilo. No es posible
determinar si:
-Vivo es ejecutable o est bloqueado.
-Un hilo ejecutable est en ese momento ejecutndose.
-Un hilo nuevo y otro que ya est muerto.
Bloqueo de objetos
Cuando un hilo llama a un mtodo sincronizado, su objeto se "bloquea", pero otros hilos son libres de llamar a los
mtodos no sincronizados de un objeto bloqueado.
Cuando un hilo abandona un mtodo sincronizado disparando una excepcin, tambin cesa el bloqueo del objeto.
Si un hilo tiene el bloqueo de un objeto y llama a otro mtodo sincronizado del mismo objeto, se otorga inmediatamente
el acceso a dicho mtodo. El hilo slo anula el bloqueo cuando sale del ltimo mtodo sincronizado.
Los mtodos wait y notify
Cuando se llama a wait, el hilo actual se bloquea y libera el bloqueo del objeto.
El mtodo wait puede disparar una InterruptedException cuando el hilo es interrumpido mientras est esperando.
Wait es un mtodo de la clase Object y no de Thread. Cuando llame a wait, bloquea el hilo actual (Donde est
corriendo el mtodo que lo invoca) y el objeto banco se desbloquea.
Si un hilo mantiene el bloqueo de varios objetos, la llamada a wait slo desbloquea aqul en el cual se efecta dicha
llamada.
Existe una diferencia entre un hilo que est esperando para obtener un mtodo sincronizado y otro que ha llamado a
wait. Una vez que un hilo llama a wait, entra en una lista de espera de ese objeto y se bloquea. Hasta que ese hilo se
elimine de esa lista, el planificador lo ignora y no tiene la posibilidad de seguir ejecutndolo. Para eliminar un hilo de la
lista de espera, es necesario que otro hilo llame a los mtodos notify en el mismo objeto; notifyAll elimina todos los hilos
de la lista de espera del objeto; notify elimina uno elegido arbitrariamente.
Una vez que un hilo ha sido eliminado de la lista de espera, vuelve a ser ejecutable.
Cuando un hilo llama a wait, ste no tiene forma de desbloquearse, y debe depositar todas sus esperanzas en los otros
hilos. Si ninguno de ellos se preocupa de desbloquearlo, nunca ms volver a ejecutarse.
-Emplear el mtodo notify si cualquiera de los hilos que estn bloqueados por ese objeto podr trabajar al ser notificado.
-Emplear el mtodo notifyAll si alguno de los hilos que estn bloqueados por ese objeto podr trabajar al ser notificado.
Ninguno de los dos tipos de notificacin tiene efecto retroactivo. Una notificacin para un hilo que no est
esperando no significa nada. Estos mtodos slo se pueden invocar desde el interior de un cdigo sincronizado, o desde
un mtodo previamente invocado desde otro sincronizado.
Puntos muertos Debe evitar las llamadas que bloquean el hilo dentro de un mtodo sincronizado, como, por ejemplo,
una llamada a una operacin de entrada/salida. En esta operacin se libera el objeto, y se bloquea el hilo. Si los dems
hilos llaman a un mtodo sincronizado de este objeto, dichos hilos tambin se bloquearn y se producir un punto
muerto.
CANCELACIN DE UN HILO
La cancelacin se solicita interrumpiendo el hilo y escribiendo dicho hilo de forma que espere y responda a las
interrupciones. Una interrupcin no fuerza a un hilo a detenerse, aunque interrumpe el sueo de un hilo durmiente.:
-interrupt(), que enva una interrupcin a un hilo;
-isInterrupted(), que comprueba si un hilo ha sido interrumpido;
-interrupted(), un mtodo static que comprueba si el hilo actual ha sido interrumpido y borra el estado de
"interrumpido" de dicho hilo.
ESPERA A QUE UN HILO FINALICE
Un hilo puede esperar a que otro finalice utilizando el mtodo join(). La forma no parametrizada espera indefinidamente a
que el hilo finalice.
public final void join(long milis) throws InterruptedException
public final void join() throws InterruptedException
Un bloque sincronizado consiste en una secuencia de instrucciones encerradas entre { . . . } y prefijadas con
synchronized (objeto), donde objeto es el objeto que debe ser bloqueado.
Mtodos estticos sincronizados
Si un hilo llama a un mtodo esttico sincronizado de una clase, todos los mtodos estticos sincronizados de esa clase
son bloqueados hasta que se retorna de la primera llamada.
Barras de progreso
Se puede construir una barra de progreso indicando un valor mnimo y mximo y una orientacin opcional:
progressBar = new JProgressBar(0, 1000);
progressBar = new JProgressBar(SwingConstants.VERTICAL, 0, 1000);
Tambin es posible establecer los valores mximo y mnimo a travs de los mtodos setMinimum y setMaximum.
Para actualizada, suprograma debe llamar a setValue. <Llamando a progressBar.setStringPainted(true); la barra de
progreso procesa el porcentaje de tarea completada y muestra una cadena con la forma "n%".