Multitarea en Java
Multitarea en Java
Índice
1 | Programación multitarea 3
4 | Finalización de subprocesos 12
5 | Sincronización 14
6 | Comunicación de subprocesos: 17
notify, notifyAll y wait
1. Programación multitarea
La programación multitarea en Java consiste en organizar un programa para que realice más de una
tarea a la vez, dividir el programa en subprocesos que pueden ejecutarse concurrentemente si se
dispone de más de un procesador o compartiendo el tiempo del procesador.
En Java los subprocesos en los que se puede organizar un programa Un programa Un programa con varios subprocesos:
se llaman “threads” Un “thread” es un flujo de control secuencial con un subproceso Subflujo 1: dos threads
Subflujo 2: tres threads
dentro de un programa. Los programas vistos hasta este momento
no son multitarea, sólo tienen un flujo de control, sólo existe un SUBFLUJO 1
único “thread”.
SUBFLUJO 2
La implementación en Java de la multitarea basada en procesos se En el método run es donde tiene lugar la acción del subproceso,
basa en la clase Thread y en la interfaz Runnable. es donde se define el código que ejecutará el subproceso. Dentro
del método run puede invocar otras funciones, usar otras clases y
declarar variables al igual que el subproceso principal.
Object
Thread <<interface>>
Runnable
+ run():void
Método Funcionalidad
void run() Punto de inicio del subproceso.
void start() Inicia la ejecución de un subproceso
invocando a su método run().
final void join() Se detiene el actual y espera a que
termine otro subproceso.
final void interrupt() Interrumpe el subproceso.
final String getName() Obtiene el nombre de un subproceso.
final setName(String nombre) Define el nombre del subproceso.
long getId() Devuelve el ID del subproceso, long
generado cuando se creó.
final String getPriority() Obtiene la prioridad de un subproceso.
final setPriority(int prioridad) Cambia la prioridad del subproceso.
final boolean isAlive() Obtiene true si un subproceso sigue en
ejecución.
final boolean isInterrupted() Obtiene true si un subproceso ha sido
interrumpido.
static void sleep(long milis) Suspende un subproceso durante los
milisegundos especificados.
static Thread currentThread() Devuelve una referencia al subproceso
que se esta ejecutando.
Thread.State getState() Devuelve el estado actual del
subproceso.
Estado Funcionalidad
new Thread()
NEW Se ha creado el subproceso pero todavía no ha
start()
arrancado. finalize()
Los subprocesos en Java después de ser creados pasan al estado • Cuando desde otro subproceso se invoca a la función Thread.
“NEW” y cuando se les invoca la función start pasan a “RUNNABLE”. join, el subproceso actual, pasa al estado “WAITING”. Permanece
En este estado, es cuando se ejecuta el método run, pero en en este hasta que el otro subproceso termina.
programación multitarea y concurrente, el tiempo del procesador se
• Cuando desde otro subproceso en un bloque sincronizado, se
reparte entre los subprocesos en este estado, de tal manera que se
invoca a la función wait , el subproceso actual, pasa al estado
garantice que todo subproceso se ejecuta.
“BLOCKED”. Permanece en este hasta que desde otro subproceso
se invoca la función notify.
El planificador de tareas (“scheduler”) es quien cuando a los
subprocesos les llega su turno, por la prioridad que tengan • Si el subproceso actual recibe la función yield, si esta en
establecida, los pasa a “RUNNING”, estando en este estado el tiempo “RUNNING”, pasa a “READY”. esto significa que deja la ejecución
de procesador correspondiente (“slice”) y cuando acabe dicho tempo y pasa a la cola de subprocesos en espera de volver a ejecución.
pasan a “READY”. Así seguirá cada subproceso hasta que termine la
• Si desde un subproceso se invoca a la función interrupt del
ejecución de run, pasando a “TERMINATED” o se produzca algunas
subproceso actual y este esta en “WAITING” o “TIME_WAITING”,
de las situaciones siguientes:
se produce la interrupción InterruptException, se procesa el
• En método run, se ejecuta la función Thread.sleep, el subproceso bloque catch que pudiera tener asociada, y a continuación sigue
cambia a “TIMED_WAITING”. Permanece en este hasta que con la ejecución de run que quedara.
terminan los milisegundos pasados como parámetro a esta
función.
• Cuando desde otro subproceso se invoca a las funciones wait, el
subproceso actual, pasa al estado “WAITING”. Permanece en este
hasta que desde otro subproceso se invoque la función notify.
Tanto wait como notify son funciones heredadas de Object.
Multitarea en Java | TELEFÓNICA / 10
La función run de estos subprocesos es la siguiente: Las funciones run y main son miembro de la misma clase que
hereda de Thread:
+ (System.
currentTimeMillis() - inicio)
+ “ milisegundos”);
}
}
}
Multitarea en Java | TELEFÓNICA / 12
4. Finalización de subprocesos
Para saber si un subproceso ha finalizado se puede utilizar la función isAlive(), devuelve true si
aún no ha finalizado y sigue en ejecución. En el código siguiente se utiliza para que el proceso principal
(función main) finalice en cuanto finalicen los tres subprocesos que se arrancan en este.
mt3.thread.isAlive());
System.out.println(“Finaliza la función
public static void main(String args[]) {
main”);
System.out.println(“Comienza función main”);
}
MiThread mt1 = new MiThread(“Subproceso
#1”);
MiThread mt2 = new MiThread(“Subproceso
#2”); También se puede esperar que finalice un subproceso utilizando la
MiThread mt3 = new MiThread(“Subproceso función join(). Esta función hace que el subproceso que invoque a
#3”); join de otro subproceso se quede esperando hasta que este finalice.
do { Por ejemplo, si en la función main se invoca a join de otro subproceso,
System.out.print(“.”); el código de la función main se queda esperando hasta que finalice
try { el subproceso para el que se invoca join. En realidad, es unir a la vida
Thread.sleep(100); de un subproceso a la de otro u otros, de ahí su nombre.
}
catch(InterruptedException exc) {
System.out.println(“Interrumpido en
main.”);
}
} while (mt1.thread.isAlive() ||
mt2.thread.isAlive() ||
Multitarea en Java | TELEFÓNICA / 13
Sus formas son: En el ejemplo el subproceso de main espera a que finalicen tres
subprocesos a los que invoca join.
public final void join() throws InterruptedException
try {
También se puede usar con estas dos formas, en las que se
mt1.thread.join();
espera a que finalice o que pase el tiempo establecido en los
parámetros. System.out.println(“Subproceso #1 se ejecuta join.”);
public final void join(long millis) throws mt2.thread.join();
InterruptedException System.out.println(“Subproceso #2 se ejecuta join.”);
public final void join(long millis, int nanos) throws mt3.thread.join();
InterruptedException System.out.println(“Subproceso #3 se ejecuta join.”);
} catch (InterruptedException exc) {
System.out.println(“Interrumpido main”);
}
System.out.println(“Finaliza la función main”);
Multitarea en Java | TELEFÓNICA / 14
5. Sincronización
Cuando varios subprocesos están accediendo al mismo recurso puede ocurrir lo que se denomina lecturas
erróneas. Por ejemplo, si un subproceso debe actualizar el valor de una variable, si ese cambio todavía no
se ha producido y otro subproceso ha leído dicho valor, el resultado es que el segundo subproceso está
trabajando con un dato erróneo, lo correcto hubiese sido esperar a que el primer subproceso actualizase
el valor, para que el segundo lo leyera.
Tres subprocesos tienen que acceder a lista para añadir, borrrar o recorrer sus elementos
Para evitar esto se utiliza la sincronización, implementada en Java
con la palabra reservada synchronized, que permite sincronizar una
función o un bloque de código. Así si un primer subproceso accede
thread01 lista.add(numero); Lista al método o código sincronizado bloquea a los demás subprocesos.
Cuando el subproceso finaliza su ejecución desbloquea a los demás
thread02 lista.remove(); 45 52 83 23 para que uno pueda acceder.
thread03 lista.listarTodos();
En el código siguiente, el método sumarArray esta sincronizado.
Dos subprocesos en su función run invocan a este método, por tanto
Se puede “proteger” acceso no simultáneo con “syncronized” o la zona de memoria o
al método que acceda a dicha zona de memoria.
sólo uno está ejecutando el código de sumarArray, el otro espera a
que el anterior acabe.
synchonized public void listarTodos() synchonized(lista){
for (Integer numero: lista){ lista.add(numero);
System.out. }
println(numero);
}
synchonized(lista){
}
lista.remove();
}
En vez de sincronizar todo un método como en el ejemplo anterior, En el ejemplo anterior, se quitaría synchronized de la definición del
se pueden sincronizar el bloque de código en el que se invoca al método sumarArray y se añadiría el bloque sincronizado para el
método. Esto tiene que ser así obligatoriamente, si el método a acceso al array de números en la llamada a la función.
utilizar es de una clase de una librería de terceros.
6. Comunicación de subprocesos:
notify, notifyAll y wait
Cuando un subproceso intenta acceder a un recurso y no puede conseguirlo, puede ponerse a la espera,
hasta que desde otro subproceso se le notifique que dicho recurso esta liberado. Por ejemplo, un
subproceso intenta añadir un elemento a una colección y está llena, puede este subproceso quedarse
en “WAITING” hasta que desde otro proceso le notifiquen que ya puede añadir el elemento.
Esta comunicación entre subprocesos se realiza con las funciones • notify(), saca de “WAITING” a uno de los subprocesos que estén
de la clase Object: en dicho estado y lo pasa a “RUNNABLE”. Su forma es:
• wait(), pone en “WAITING” al subproceso que invoca la función,
hasta que otro invoque a notify o notifyAll. Sus formas son: public final void notify()
public final void wait() throws InterruptedException
• notifyAll(), saca de “WAITING” a todos los subprocesos que
También se puede usar con estas dos formas, en las que se
estuvieran en este estado y los pasa “RUNNABLE”
espera a que otro invoque notify o notifyAll o pase el tiempo
especificado.
publicfinalvoidwait(longtimeout)throwsInterruptedException
El método suspend y el método stop causaban problemas, no En el ejemplo siguiente se muestra una posible forma de realizarlo.
funcionaba adecuadamente, y se decidió ponerlos “decrepated”, el
método resume como complementario a suspend pasó también a
este estado.
Sin utilizar estas funciones, estas operaciones se pueden realizar class MiThread implements Runnable {
con comprobaciones periódicas en el método run sobre si tiene que Thread thread;
suspender, reanudar o parar su propia ejecución. Se suelen utilizar boolean suspendido;
dos variables, una para suspender y reanudar, la otra para parar. boolean parado;
MiThread(String nombre) {
thread = new Thread(this, nombre);
suspendido = false;
parado = false;
thread.start();
}
Multitarea en Java | TELEFÓNICA / 19