Threads

48
Hilos (Threads) Un hilo representa un proceso individual ejecutándose en un sistema . A veces se les llama procesos ligeros o contextos de ejecución. Típicamente, cada thread controla un único aspecto dentro de un programa, como puede ser: supervisar la entrada en un determinado periférico o controlar toda la entrada/salida del disco. Todos los threads comparten los mismos recursos, al contrario que los procesos en donde cada uno tiene su propia copia de código y datos (separados unos de otros). Gráficamente, los threads se parecen en su funcionamiento a lo que muestra la figura siguiente: Cómo s e usan: Para usarlo solamente hay que crear un objeto de la clase Thread y pasarle un Runnable. Un Runnable es un objeto que, "implementando" esa interfaz, promete al mundo contar con un método run(). El nuevo thread iniciado comenzará su ejecución saltando a este método y cuando éste termine el thread terminará. Ejemplo: Thread t = new Thread("Thread para contar", new Runnable() { void run() { for(int i = 1 ; i <= 10 ; i++) System.out.println(i); } }); t.start(); /* Acá, para ejemplificar, llamamos a un método que tarda, * por ejemplo porque espera que se tipee enter. Mientras * tanto, en la pantalla va apareciendo la cuenta hasta diez * que sucede en el thread. */ in.readLine(); ¡No confundir el método start con el método run! El método run contiene el código a ser ejecutado “asíncronamente” en otro thread, mientras que el método start es el que crea el

description

Hilos en java, teoria basica.

Transcript of Threads

Hilos (Threads)

Un hilo representa un proceso individual ejecutándose en un sistema. A veces se les llama

procesos ligeros o contextos de ejecución. Típicamente, cada thread controla un único aspecto

dentro de un programa, como puede ser: supervisar la entrada en un determinado periférico o

controlar toda la entrada/salida del disco. Todos los threads comparten los mismos recursos, al

contrario que los procesos en donde cada uno tiene su propia copia de código y datos (separados

unos de otros). Gráficamente, los threads se parecen en su funcionamiento a lo que muestra la

figura siguiente:

Cómo s e usan:

Para usarlo solamente hay que crear un objeto de la clase Thread y pasarle un Runnable. Un

Runnable es un objeto que, "implementando" esa interfaz, promete al mundo contar con un

método run(). El nuevo thread iniciado comenzará su ejecución saltando a este método y cuando

éste termine el thread terminará.

Ejemplo:

Thread t = new Thread("Thread para contar", new Runnable() {

void run()

{

for(int i = 1 ; i <= 10 ; i++)

System.out.println(i);

}

});

t.start();

/* Acá, para ejemplificar, llamamos a un método que tarda,

* por ejemplo porque espera que se tipee enter. Mientras

* tanto, en la pantalla va apareciendo la cuenta hasta diez

* que sucede en el thread.

*/

in.readLine();

¡No confundir el método start con el método run! El método run contiene el código a ser

ejecutado “asíncronamente” en otro thread, mientras que el método start es el que crea el

thread y en algún punto hace que ese thread ejecute lo que está en run. Este método

devuelve el control inmediatamente. Pero si mezclamos todo y ejecutamos directamente

run(), el código se ejecutará en el thread actual!

El método start() devuelve el control inmediatamente, mientras tanto, el nuevo thread inicia su

recorrido por el método run(). ¿Hasta cuándo? Hasta que termina ese método, cuando sale,

termina el thread. Si un thread necesita esperar a que otro termina (por ejemplo el thread padre

esperar a que termine el hijo) puede usar el método join(). ¿Por qué se llama así? Bueno, crear

un proceso es como una bifuración, se abren dos caminos, que uno espere a otro es lo contrario,

una unificación.

Sincronización

La necesidad

La cuestión cuando se trabaja con threads es que la ejecución avanza en varias partes del

programa a la vez. Cada una de esas ejecuciones simultáneas pueden tocar los mismos

objetos. Eso a veces es un problema. Un ejemplo: Suponga que un thread encola pedidos e

incrementa un contador. Existen además 50 threads que se fijan si el contador es mayor que

cero y si lo es retiran un pedido, decrementan el contador, y procesan la tarea. Supongamos

que hay un pedido en la cola, el contador vale 1, y que sucede lo siguiente:

1. El thread A comprueba que el contador vale más que cero.

2. El thread B comprueba que el contador vale más que cero.

3. Basado en su comprobación el thread B decrementa el contador y toma el pedido.

4. Basado en su comprobación el thread A decrementa el contador y toma el ped...

auch, ya no hay pedido!

¿Qué pasó acá? El thread A miró, vio algo y se dispuso a actuar, pero cuando actuó alguien

se le había metido en el medio. El mundo ya no era el que era cuando él tomó la decisión de

actuar. El problema, generalizado, es el espacio de tiempo que hay entre mirar y actuar,

cuando el mundo en el que se mira es compartido por más de un actor. A este tipo de

problemas se les llama condición de carrera (en inglés “race condition”), porque son como

una competencia.

La solución del problema

Para evitar el caso que expuse lo que se hace es establecer un “lock”, un bloqueo. En Java

cada objeto tiene asignado algo que se le llama “monitor”. Mediante la palabra clave

synchronized un thread puede “tomar” el monitor. Si otro thread intenta tomar el monitor

del mismo objeto, el segundo thread se bloquea hasta que el primero suelte ese monitor.

Entonces, el ejemplo anterior modificado por la sabiduría de los bloqueos quedaría así:

1. El thread A intenta tomar el monitor del objeto de la cola. Lo consigue: es suyo.

2. El thread A comprueba que el contador vale más que cero.

3. El thread B intenta tomar el monitor del objeto de la cola. No lo consigue: lo tiene

A, inicia espera.

4. Basado en su comprobación el thread A decrementa el contador y toma el pedido.

5. El thread A libera el monitor.

6. Al quedar el monitor liberado el thread B continúa. Ahora tiene el monitor.

7. El thread B comprueba que el contador vale cero.

8. Basado en su comprobación el thread B ve que no tiene nada que hacer.

9. El thread B libera el monitor.

Sintaxis para establecer locks

Como se dijo, la toma de un monitor se debe hacer con la palabra clave synchronized.

Esta palabra tiene dos formas de ser usada. La primera y más básica forma de usar

synchronized es la siguiente:

synchronized(objeto)

{

// instrucciones

}

El intérprete Java toma el monitor del objeto y ejecuta las instrucciones del bloque. En

cualquier caso en el que se salga del bloque el monitor es liberado. Esto incluye la salida

por causa de alguna excepción que se produzca. El mecanismo asegura que no exista el

riesgo de que se salga de ese bloque sin liberar el monitor, lo que probablemente tarde o

temprano haría que se congele todo el programa.

¿Que por qué se llama monitor? Es muy simple, es porqu_ \_-_- /-\. '-|-/\-¬ |-\ /-/| --||\|-. '-|-/\-

¬ |-\ /-/| \/-¬ --||\|-. Obvio, ¿no?

Es de notar que el uso del monitor de un objeto no tiene nada que ver con el uso del objeto

mismo. Mientras el código que deba ser “mutuamente excluido” se sincronice sobre el

mismo objeto... ¡no importa qué papel tome ese objeto en el gran esquema de las cosas!

Retomando el ejemplo anterior, una implementación del método que toma un pedido podría

ser (antes de aplicar la sabiduría de threads aquí explicada):

class MesaDeEntradas

{

int n = 0;

Queue<Pedido> cola;

Pedido retirarPedido()

{

if(n > 0)

{

n--;

return cola.remove(0);

}

return null;

}

}

Los dos momentos críticos en los que dos threads separados pueden molestarse entre sí son

claramente el momento del if y el momento en el que se invoca al método remove(0).

Cuando un thread está preguntando el valor de n no debe haber ninguno otro ni

preguntando el valor de n, ni removiendo el valor de la pila. Esto podemos lograrlo

tomando el monitor antes de todo este código y liberándolo después:

class MesaDeEntradas

{

int n = 0;

Queue<Pedido> cola;

Pedido retirarPedido()

{

synchronized(this)

{

if(n > 0)

{

n--;

return cola.remove(0);

}

return null;

}

}

}

Hay dos salidas posibles del bloque sincronizado, ya que hay dos instrucciones return.

Pero no hay problema con eso, ya que como se dijo cualquiera sea la forma en que se

abandone el bloque, el monitor tomado será liberado correctamente.

Es interesante ver que el hecho de sincronizar sobre this no obedece sino a una

convención completamente arbitraria decidida por el programador. Ya que el objeto this

es el que está de alguna manera englobando a toda la funcionalidad provista, es lógico

usarlo para sincronizar. Pero hay que notar que cualquier otro método de esta clase que use

this en un bloque synchronized se sincronizará también con este bloque! Si esto no es lo

que se quiere se deberá entonces elegir cualquier otro objeto. A veces tiene sentido crear un

objeto solamente para usar su monitor.

Como es muy común querer usar this para sincronizarse, se introdujo una ayudita

sintáctica que consiste en una forma abreviada de tomar el monitor de la instancia en la que

ese está ejecutando un método. De esta manera...

synchronized void m()

{

// ...

}

... equivale a ...

void m()

{

synchronized(this)

{

// ...

}

}

Por lo tanto podemos reescribir el código anterior como...

class MesaDeEntradas

{

int n = 0;

Queue<Pedido> cola;

synchronized Pedido retirarPedido()

{

if(n > 0)

{

n--;

return cola.remove(0);

}

return null;

}

}

Esperando locks

En el ejemplo que di más arriba teníamos threads que periódicamente se fijaban si un

contador era mayor a 0 para saber si había pedidos a procesar. ¿Cada cuánto debería uno

fijarse si hay pedidos? ¿A cada minuto? ¿Cada tres? ¿segundos?

En el caso de los ejemplos anteriores, el método retirarPedido devuelve null si no hay

todavía ningún pedido esperando (es decir, n == 0). Ahora, supongamos que tenemos 10

threads creados al solo efecto de procesar pedidos. Cada uno de esos threads querrá

quedarse esperando si no hay un pedido.

La manera torpe de hacerlo:

Pedido p = null;

while(true)

{

p = m.retirarPedido();

if(p != null)

break;

else

Thread.sleep(1000); // esperamos un segundo

}

Pero creo que es claro que no es eficiente hacerlo así. Además en muchos casos no

conviene esperar todo un segundo para procesar lo que hay que procesar.

Las APIs de sincronización en casi todos los lenguajes y plataformas siempre proveen

mecanismos de espera de condiciones. En el caso de Java la cosa se hace como explico a

continuación. Y acá es donde aparecen wait y notify.

notify:

Como disparar el tiro que inicia una carrera.

wait:

El corredor que espera ese tiro para correr.

El thread se “duerme” sobre un monitor. Cuando otro thread “sacude” el monitor, el bello

durmiente se despierta y continúa. Esto se hace con las operaciones wait, notify y

notifyAll.

Entonces, podría parecer que la cosa se resuelve así: En la "MesaDeEntradas", cuando llega

un pedido, se deberá hacer:

class MesaDeEntradas

{

synchronized void métodoQueRecibeUnPedido(Pedido p)

{

cola.add(p);

notifyAll();

}

}

Y si no somos muy conocedores del tema nos podría parecer que el otro lado del código (el

consumidor de la información) se vería algo como esto:

m.wait();

Pedido p = m.retirarPedido();

... pero ¿cómo vamos a esperar sin preguntar antes? ¿Qué pasa si ya había un pedido? Nos

quedaríamos esperando en vano, entonces mejor preguntamos:

// si no hay un pedido, esperamos.

if(!m.hayPedido())

m.wait();

Pedido p = retirarPedido();

Si algo sacó usted de la lectura de la sección anterior, debería haber descubierto el

problema que aparece. Examinamos el mundo (preguntando si hay pedido)... y después

actuamos (iniciar la espera). ¿Qué pasa si el pedido llega en el medio y el notify (en otro

thread) sucede antes de que nos pongamos a esperar? Nos quedaríamos esperando un notify

que nunca llegará.

Por lo dicho es importante tomar "el monitor" del objeto a esperar antes de decidir hacerlo.

Pero en el momento inmediatamente después de iniciada la espera el monitor debe ser

soltado para que el notify (que intentará tomar ese mismo monitor) pueda suceder. Sería

imposible sin ayuda del lenguaje soltar algo mientras estamos en un wait(), ya que la

ejecución está suspendida. Por eso, al entrar en el wait se libera atómicamente el monitor.

La entrada en el wait, y soltar el "lock" sobre el objeto suceden como una operación

indivisible. Repito: Al llamar a wait() sobre un objeto... automáticamente se libera el

monitor (¡sin haber salido del synchronized!). Cuando wait termina, automáticamente se

recupera el monitor.

Entoces aplicando todo esto correctamente la espera se escribe así:

Pedido p = null;

synchronized(m)

{

m.wait();

p = retirarPedido();

}

java.lang Class Thread

java.lang.Object

java.lang.Thread

All Implemented Interfaces: Runnable

public class Thread

extends Object

implements Runnable

A thread is a thread of execution in a program. The Java Virtual Machine allows an

application to have multiple threads of execution running concurrently.

Every thread has a priority. Threads with higher priority are executed in preference to

threads with lower priority. Each thread may or may not also be marked as a daemon.

When code running in some thread creates a new Thread object, the new thread has its

priority initially set equal to the priority of the creating thread, and is a daemon thread if

and only if the creating thread is a daemon.

When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which

typically calls the method named main of some designated class). The Java Virtual

Machine continues to execute threads until either of the following occurs:

• The exit method of class Runtime has been called and the security manager has

permitted the exit operation to take place.

• All threads that are not daemon threads have died, either by returning from the call

to the run method or by throwing an exception that propagates beyond the run

method.

There are two ways to create a new thread of execution. One is to declare a class to be a

subclass of Thread. This subclass should override the run method of class Thread. An

instance of the subclass can then be allocated and started. For example, a thread that

computes primes larger than a stated value could be written as follows:

class PrimeThread extends Thread {

long minPrime;

PrimeThread(long minPrime) {

this.minPrime = minPrime;

}

public void run() {

// compute primes larger than minPrime

. . .

}

}

The following code would then create a thread and start it running:

PrimeThread p = new PrimeThread(143);

p.start();

The other way to create a thread is to declare a class that implements the Runnable

interface. That class then implements the run method. An instance of the class can then be

allocated, passed as an argument when creating Thread, and started. The same example in

this other style looks like the following:

class PrimeRun implements Runnable {

long minPrime;

PrimeRun(long minPrime) {

this.minPrime = minPrime;

}

public void run() {

// compute primes larger than minPrime

. . .

}

}

The following code would then create a thread and start it running:

PrimeRun p = new PrimeRun(143);

new Thread(p).start();

Every thread has a name for identification purposes. More than one thread may have the

same name. If a name is not specified when a thread is created, a new name is generated for

it.

Since: JDK1.0

See Also:

Runnable, Runtime.exit(int), run(), stop()

Field Summary

static int MAX_PRIORITY The maximum priority that a thread can have.

static int MIN_PRIORITY The minimum priority that a thread can have.

static int NORM_PRIORITY The default priority that is assigned to a thread.

Constructor Summary

Thread()

Allocates a new Thread object.

Thread(Runnable target)

Allocates a new Thread object.

Thread(Runnable target, String name)

Allocates a new Thread object.

Thread(String name)

Allocates a new Thread object.

Thread(ThreadGroup group, Runnable target)

Allocates a new Thread object.

Thread(ThreadGroup group, Runnable target, String name)

Allocates a new Thread object so that it has target as its run object, has the

specified name as its name, and belongs to the thread group referred to by group.

Thread(ThreadGroup group, Runnable target, String name, long stackSize)

Allocates a new Thread object so that it has target as its run object, has the

specified name as its name, belongs to the thread group referred to by group, and has the

specified stack size.

Thread(ThreadGroup group, String name)

Allocates a new Thread object.

Method Summary

static int activeCount() Returns the number of active threads in the current thread's thread

group.

void checkAccess() Determines if the currently running thread has permission to

modify this thread.

int countStackFrames()

Deprecated. The definition of this call depends on suspend(),

which is deprecated. Further, the results of this call were never well-

defined.

static Thread currentThread() Returns a reference to the currently executing thread object.

void destroy() Destroys this thread, without any cleanup.

static void dumpStack() Prints a stack trace of the current thread.

static int enumerate(Thread[] tarray)

Copies into the specified array every active thread in the current

thread's thread group and its subgroups.

ClassLoader getContextClassLoader() Returns the context ClassLoader for this Thread.

String getName()

Returns this thread's name.

int getPriority()

Returns this thread's priority.

ThreadGroup getThreadGroup()

Returns the thread group to which this thread belongs.

static boolean holdsLock(Object obj)

Returns true if and only if the current thread holds the monitor

lock on the specified object.

void interrupt()

Interrupts this thread.

static boolean interrupted()

Tests whether the current thread has been interrupted.

boolean isAlive() Tests if this thread is alive.

boolean isDaemon() Tests if this thread is a daemon thread.

boolean isInterrupted() Tests whether this thread has been interrupted.

void join() Waits for this thread to die.

void join(long millis)

Waits at most millis milliseconds for this thread to die.

void join(long millis, int nanos)

Waits at most millis milliseconds plus nanos nanoseconds for

this thread to die.

void resume()

Deprecated. This method exists solely for use with suspend(),

which has been deprecated because it is deadlock-prone. For more

information, see Why are Thread.stop, Thread.suspend and

Thread.resume Deprecated?.

void run()

If this thread was constructed using a separate Runnable run

object, then that Runnable object's run method is called; otherwise, this

method does nothing and returns.

void setContextClassLoader(ClassLoader cl)

Sets the context ClassLoader for this Thread.

void setDaemon(boolean on)

Marks this thread as either a daemon thread or a user thread.

void setName(String name)

Changes the name of this thread to be equal to the argument name.

void setPriority(int newPriority) Changes the priority of this thread.

static void sleep(long millis)

Causes the currently executing thread to sleep (temporarily cease

execution) for the specified number of milliseconds.

static void sleep(long millis, int nanos)

Causes the currently executing thread to sleep (cease execution)

for the specified number of milliseconds plus the specified number of

nanoseconds.

void start()

Causes this thread to begin execution; the Java Virtual Machine

calls the run method of this thread.

void stop()

Deprecated. This method is inherently unsafe. Stopping a thread

with Thread.stop causes it to unlock all of the monitors that it has locked

(as a natural consequence of the unchecked ThreadDeath exception

propagating up the stack). If any of the objects previously protected by

these monitors were in an inconsistent state, the damaged objects

become visible to other threads, potentially resulting in arbitrary

behavior. Many uses of stop should be replaced by code that simply

modifies some variable to indicate that the target thread should stop

running. The target thread should check this variable regularly, and

return from its run method in an orderly fashion if the variable indicates

that it is to stop running. If the target thread waits for long periods (on a

condition variable, for example), the interrupt method should be used

to interrupt the wait. For more information, see Why are Thread.stop,

Thread.suspend and Thread.resume Deprecated?.

void stop(Throwable obj)

Deprecated. This method is inherently unsafe. See stop() (with

no arguments) for details. An additional danger of this method is that it

may be used to generate exceptions that the target thread is unprepared

to handle (including checked exceptions that the thread could not

possibly throw, were it not for this method). For more information, see

Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.

void suspend()

Deprecated. This method has been deprecated, as it is inherently

deadlock-prone. If the target thread holds a lock on the monitor

protecting a critical system resource when it is suspended, no thread can

access this resource until the target thread is resumed. If the thread that

would resume the target thread attempts to lock this monitor prior to

calling resume, deadlock results. Such deadlocks typically manifest

themselves as "frozen" processes. For more information, see Why are

Thread.stop, Thread.suspend and Thread.resume Deprecated?.

String toString() Returns a string representation of this thread, including the thread's

name, priority, and thread group.

static void yield()

Causes the currently executing thread object to temporarily pause

and allow other threads to execute.

Methods inherited from class java.lang.Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Field Detail

MIN_PRIORITY

public static final int MIN_PRIORITY

The minimum priority that a thread can have.

See Also: Constant Field Values

NORM_PRIORITY

public static final int NORM_PRIORITY

The default priority that is assigned to a thread.

See Also: Constant Field Values

MAX_PRIORITY

public static final int MAX_PRIORITY

The maximum priority that a thread can have.

See Also: Constant Field Values

Constructor Detail

Thread

public Thread()

Allocates a new Thread object. This constructor has the same effect as

Thread(null, null, gname), where gname is a newly generated name.

Automatically generated names are of the form "Thread-"+n, where n is an integer.

See Also: Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)

Thread

public Thread(Runnable target)

Allocates a new Thread object. This constructor has the same effect as

Thread(null, target, gname), where gname is a newly generated name.

Automatically generated names are of the form "Thread-"+n, where n is an integer.

Parameters:

target - the object whose run method is called.

See Also: Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)

Thread

public Thread(ThreadGroup group,

Runnable target)

Allocates a new Thread object. This constructor has the same effect as

Thread(group, target, gname), where gname is a newly generated name.

Automatically generated names are of the form "Thread-"+n, where n is an integer.

Parameters:

group - the thread group.

target - the object whose run method is called.

Throws: SecurityException - if the current thread cannot create a thread in the specified

thread group.

See Also: Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)

Thread

public Thread(String name)

Allocates a new Thread object. This constructor has the same effect as

Thread(null, null, name).

Parameters:

name - the name of the new thread.

See Also: Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)

Thread

public Thread(ThreadGroup group,

String name)

Allocates a new Thread object. This constructor has the same effect as Thread(group, null, name)

Parameters:

group - the thread group.

name - the name of the new thread.

Throws: SecurityException - if the current thread cannot create a thread in the specified

thread group.

See Also: Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)

Thread

public Thread(Runnable target,

String name)

Allocates a new Thread object. This constructor has the same effect as

Thread(null, target, name).

Parameters:

target - the object whose run method is called.

name - the name of the new thread.

See Also: Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)

Thread

public Thread(ThreadGroup group,

Runnable target,

String name)

Allocates a new Thread object so that it has target as its run object, has the

specified name as its name, and belongs to the thread group referred to by group.

If group is null and there is a security manager, the group is determined by the

security manager's getThreadGroup method. If group is null and there is not a

security manager, or the security manager's getThreadGroup method returns null,

the group is set to be the same ThreadGroup as the thread that is creating the new

thread.

If there is a security manager, its checkAccess method is called with the

ThreadGroup as its argument. This may result in a SecurityException.

If the target argument is not null, the run method of the target is called when

this thread is started. If the target argument is null, this thread's run method is

called when this thread is started.

The priority of the newly created thread is set equal to the priority of the thread

creating it, that is, the currently running thread. The method setPriority may be

used to change the priority to a new value.

The newly created thread is initially marked as being a daemon thread if and only if

the thread creating it is currently marked as a daemon thread. The method

setDaemon may be used to change whether or not a thread is a daemon.

Parameters:

group - the thread group.

target - the object whose run method is called.

name - the name of the new thread.

Throws: SecurityException - if the current thread cannot create a thread in the specified

thread group.

See Also:

Runnable.run(), run(), setDaemon(boolean), setPriority(int),

ThreadGroup.checkAccess(), SecurityManager.checkAccess(java.lang.Thread)

Thread

public Thread(ThreadGroup group,

Runnable target,

String name,

long stackSize)

Allocates a new Thread object so that it has target as its run object, has the

specified name as its name, belongs to the thread group referred to by group, and

has the specified stack size.

This constructor is identical to Thread(ThreadGroup,Runnable,String) with the

exception of the fact that it allows the thread stack size to be specified. The stack

size is the approximate number of bytes of address space that the virtual machine is

to allocate for this thread's stack. The effect of the stackSize parameter, if any,

is highly platform dependent.

On some platforms, specifying a higher value for the stackSize parameter may

allow a thread to achieve greater recursion depth before throwing a

StackOverflowError. Similarly, specifying a lower value may allow a greater

number of threads to exist concurrently without throwing an an OutOfMemoryError

(or other internal error). The details of the relationship between the value of the

stackSize parameter and the maximum recursion depth and concurrency level are

platform-dependent. On some platforms, the value of the stackSize parameter

may have no effect whatsoever.

The virtual machine is free to treat the stackSize parameter as a suggestion. If the

specified value is unreasonably low for the platform, the virtual machine may

instead use some platform-specific minimum value; if the specified value is

unreasonably high, the virtual machine may instead use some platform-specific

maximum. Likewise, the virtual machine is free to round the specified value up or

down as it sees fit (or to ignore it completely).

Specifying a value of zero for the stackSize parameter will cause this constructor

to behave exactly like the Thread(ThreadGroup, Runnable, String)

constructor.

Due to the platform-dependent nature of the behavior of this constructor, extreme

care should be exercised in its use. The thread stack size necessary to perform a

given computation will likely vary from one JRE implementation to another. In light

of this variation, careful tuning of the stack size parameter may be required, and the

tuning may need to be repeated for each JRE implementation on which an

application is to run.

Implementation note: Java platform implementers are encouraged to document their

implementation's behavior with respect to the stackSize parameter.

Parameters:

group - the thread group.

target - the object whose run method is called.

name - the name of the new thread.

stackSize - the desired stack size for the new thread, or zero to indicate that this

parameter is to be ignored.

Throws: SecurityException - if the current thread cannot create a thread in the specified

thread group.

Method Detail

currentThread

public static Thread currentThread()

Returns a reference to the currently executing thread object.

Returns: the currently executing thread.

yield

public static void yield()

Causes the currently executing thread object to temporarily pause and allow other

threads to execute.

sleep

public static void sleep(long millis)

throws InterruptedException

Causes the currently executing thread to sleep (temporarily cease execution) for the

specified number of milliseconds. The thread does not lose ownership of any

monitors.

Parameters:

millis - the length of time to sleep in milliseconds.

Throws: InterruptedException - if another thread has interrupted the current thread. The

interrupted status of the current thread is cleared when this exception is thrown.

See Also: Object.notify()

sleep

public static void sleep(long millis,

int nanos)

throws InterruptedException

Causes the currently executing thread to sleep (cease execution) for the specified

number of milliseconds plus the specified number of nanoseconds. The thread does

not lose ownership of any monitors.

Parameters:

millis - the length of time to sleep in milliseconds.

nanos - 0-999999 additional nanoseconds to sleep.

Throws: IllegalArgumentException - if the value of millis is negative or the value of nanos is

not in the range 0-999999.

InterruptedException - if another thread has interrupted the current thread. The

interrupted status of the current thread is cleared when this exception is thrown.

See Also: Object.notify()

start

public void start()

Causes this thread to begin execution; the Java Virtual Machine calls the run

method of this thread.

The result is that two threads are running concurrently: the current thread (which

returns from the call to the start method) and the other thread (which executes its

run method).

Throws: IllegalThreadStateException - if the thread was already started.

See Also:

run(), stop()

run

public void run()

If this thread was constructed using a separate Runnable run object, then that

Runnable object's run method is called; otherwise, this method does nothing and

returns.

Subclasses of Thread should override this method.

Specified by: run in interface Runnable

See Also:

start(), stop(), Thread(java.lang.ThreadGroup, java.lang.Runnable,

java.lang.String), Runnable.run()

stop

public final void stop()

Deprecated. This method is inherently unsafe. Stopping a thread with Thread.stop

causes it to unlock all of the monitors that it has locked (as a natural consequence

of the unchecked ThreadDeath exception propagating up the stack). If any of the

objects previously protected by these monitors were in an inconsistent state, the

damaged objects become visible to other threads, potentially resulting in arbitrary

behavior. Many uses of stop should be replaced by code that simply modifies some

variable to indicate that the target thread should stop running. The target thread

should check this variable regularly, and return from its run method in an orderly

fashion if the variable indicates that it is to stop running. If the target thread waits

for long periods (on a condition variable, for example), the interrupt method

should be used to interrupt the wait. For more information, see Why are

Thread.stop, Thread.suspend and Thread.resume Deprecated?.

Forces the thread to stop executing.

If there is a security manager installed, its checkAccess method is called with this

as its argument. This may result in a SecurityException being raised (in the

current thread).

If this thread is different from the current thread (that is, the current thread is trying

to stop a thread other than itself), the security manager's checkPermission method

(with a RuntimePermission("stopThread") argument) is called in addition.

Again, this may result in throwing a SecurityException (in the current thread).

The thread represented by this thread is forced to stop whatever it is doing

abnormally and to throw a newly created ThreadDeath object as an exception.

It is permitted to stop a thread that has not yet been started. If the thread is

eventually started, it immediately terminates.

An application should not normally try to catch ThreadDeath unless it must do

some extraordinary cleanup operation (note that the throwing of ThreadDeath

causes finally clauses of try statements to be executed before the thread officially

dies). If a catch clause catches a ThreadDeath object, it is important to rethrow the

object so that the thread actually dies.

The top-level error handler that reacts to otherwise uncaught exceptions does not

print out a message or otherwise notify the application if the uncaught exception is

an instance of ThreadDeath.

Throws: SecurityException - if the current thread cannot modify this thread.

See Also:

interrupt(), checkAccess(), run(), start(), ThreadDeath, ThreadGroup.uncaughtException(java.lang.Thread,

java.lang.Throwable), SecurityManager.checkAccess(Thread), SecurityManager.checkPermission(java.security.Permission)

stop

public final void stop(Throwable obj)

Deprecated. This method is inherently unsafe. See stop() (with no arguments) for

details. An additional danger of this method is that it may be used to generate

exceptions that the target thread is unprepared to handle (including checked

exceptions that the thread could not possibly throw, were it not for this method).

For more information, see Why are Thread.stop, Thread.suspend and

Thread.resume Deprecated?.

Forces the thread to stop executing.

If there is a security manager installed, the checkAccess method of this thread is

called, which may result in a SecurityException being raised (in the current

thread).

If this thread is different from the current thread (that is, the current thread is trying

to stop a thread other than itself) or obj is not an instance of ThreadDeath, the

security manager's checkPermission method (with the

RuntimePermission("stopThread") argument) is called in addition. Again, this

may result in throwing a SecurityException (in the current thread).

If the argument obj is null, a NullPointerException is thrown (in the current

thread).

The thread represented by this thread is forced to complete whatever it is doing

abnormally and to throw the Throwable object obj as an exception. This is an

unusual action to take; normally, the stop method that takes no arguments should

be used.

It is permitted to stop a thread that has not yet been started. If the thread is

eventually started, it immediately terminates.

Parameters:

obj - the Throwable object to be thrown.

Throws: SecurityException - if the current thread cannot modify this thread.

See Also:

interrupt(), checkAccess(), run(), start(), stop(),

SecurityManager.checkAccess(Thread), SecurityManager.checkPermission(java.security.Permission)

interrupt

public void interrupt()

Interrupts this thread.

First the checkAccess method of this thread is invoked, which may cause a

SecurityException to be thrown.

If this thread is blocked in an invocation of the wait(), wait(long), or

wait(long, int) methods of the Object class, or of the join(), join(long),

join(long, int), sleep(long), or sleep(long, int), methods of this class,

then its interrupt status will be cleared and it will receive an

InterruptedException.

If this thread is blocked in an I/O operation upon an interruptible channel then the

channel will be closed, the thread's interrupt status will be set, and the thread will

receive a ClosedByInterruptException.

If this thread is blocked in a Selector then the thread's interrupt status will be set

and it will return immediately from the selection operation, possibly with a non-zero

value, just as if the selector's wakeup method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

Throws: SecurityException - if the current thread cannot modify this thread

interrupted

public static boolean interrupted()

Tests whether the current thread has been interrupted. The interrupted status of the

thread is cleared by this method. In other words, if this method were to be called

twice in succession, the second call would return false (unless the current thread

were interrupted again, after the first call had cleared its interrupted status and

before the second call had examined it).

Returns:

true if the current thread has been interrupted; false otherwise.

See Also: isInterrupted()

isInterrupted

public boolean isInterrupted()

Tests whether this thread has been interrupted. The interrupted status of the thread

is unaffected by this method.

Returns:

true if this thread has been interrupted; false otherwise.

See Also: interrupted()

destroy

public void destroy()

Destroys this thread, without any cleanup. Any monitors it has locked remain

locked. (This method is not implemented.)

isAlive

public final boolean isAlive()

Tests if this thread is alive. A thread is alive if it has been started and has not yet

died.

Returns:

true if this thread is alive; false otherwise.

suspend

public final void suspend()

Deprecated. This method has been deprecated, as it is inherently deadlock-prone.

If the target thread holds a lock on the monitor protecting a critical system resource

when it is suspended, no thread can access this resource until the target thread is

resumed. If the thread that would resume the target thread attempts to lock this

monitor prior to calling resume, deadlock results. Such deadlocks typically

manifest themselves as "frozen" processes. For more information, see Why are

Thread.stop, Thread.suspend and Thread.resume Deprecated?.

Suspends this thread.

First, the checkAccess method of this thread is called with no arguments. This may

result in throwing a SecurityException (in the current thread).

If the thread is alive, it is suspended and makes no further progress unless and until

it is resumed.

Throws: SecurityException - if the current thread cannot modify this thread.

See Also: checkAccess()

resume

public final void resume()

Deprecated. This method exists solely for use with suspend(), which has been

deprecated because it is deadlock-prone. For more information, see Why are

Thread.stop, Thread.suspend and Thread.resume Deprecated?.

Resumes a suspended thread.

First, the checkAccess method of this thread is called with no arguments. This may

result in throwing a SecurityException (in the current thread).

If the thread is alive but suspended, it is resumed and is permitted to make progress

in its execution.

Throws: SecurityException - if the current thread cannot modify this thread.

See Also:

checkAccess(), suspend()

setPriority

public final void setPriority(int newPriority)

Changes the priority of this thread.

First the checkAccess method of this thread is called with no arguments. This may

result in throwing a SecurityException.

Otherwise, the priority of this thread is set to the smaller of the specified

newPriority and the maximum permitted priority of the thread's thread group.

Parameters:

newPriority - priority to set this thread to

Throws:

IllegalArgumentException - If the priority is not in the range MIN_PRIORITY to

MAX_PRIORITY.

SecurityException - if the current thread cannot modify this thread.

See Also:

getPriority(), checkAccess(), getPriority(), getThreadGroup(),

MAX_PRIORITY, MIN_PRIORITY, ThreadGroup.getMaxPriority()

getPriority

public final int getPriority()

Returns this thread's priority.

Returns: this thread's priority.

See Also:

setPriority(int), setPriority(int)

setName

public final void setName(String name)

Changes the name of this thread to be equal to the argument name.

First the checkAccess method of this thread is called with no arguments. This may

result in throwing a SecurityException.

Parameters:

name - the new name for this thread.

Throws: SecurityException - if the current thread cannot modify this thread.

See Also:

getName(), checkAccess(), getName()

getName

public final String getName()

Returns this thread's name.

Returns: this thread's name.

See Also:

setName(java.lang.String), setName(java.lang.String)

getThreadGroup

public final ThreadGroup getThreadGroup()

Returns the thread group to which this thread belongs. This method returns null if

this thread has died (been stopped).

Returns: this thread's thread group.

activeCount

public static int activeCount()

Returns the number of active threads in the current thread's thread group.

Returns: the number of active threads in the current thread's thread group.

enumerate

public static int enumerate(Thread[] tarray)

Copies into the specified array every active thread in the current thread's thread

group and its subgroups. This method simply calls the enumerate method of the

current thread's thread group with the array argument.

First, if there is a security manager, that enumerate method calls the security

manager's checkAccess method with the thread group as its argument. This may

result in throwing a SecurityException.

Parameters:

tarray - an array of Thread objects to copy to

Returns: the number of threads put into the array

Throws:

SecurityException - if a security manager exists and its checkAccess method

doesn't allow the operation.

See Also:

ThreadGroup.enumerate(java.lang.Thread[]), SecurityManager.checkAccess(java.lang.ThreadGroup)

countStackFrames

public int countStackFrames()

Deprecated. The definition of this call depends on suspend(), which is deprecated.

Further, the results of this call were never well-defined.

Counts the number of stack frames in this thread. The thread must be suspended.

Returns: the number of stack frames in this thread.

Throws: IllegalThreadStateException - if this thread is not suspended.

join

public final void join(long millis)

throws InterruptedException

Waits at most millis milliseconds for this thread to die. A timeout of 0 means to

wait forever.

Parameters:

millis - the time to wait in milliseconds.

Throws: InterruptedException - if another thread has interrupted the current thread. The

interrupted status of the current thread is cleared when this exception is thrown.

join

public final void join(long millis,

int nanos)

throws InterruptedException

Waits at most millis milliseconds plus nanos nanoseconds for this thread to die.

Parameters:

millis - the time to wait in milliseconds.

nanos - 0-999999 additional nanoseconds to wait.

Throws: IllegalArgumentException - if the value of millis is negative the value of nanos is

not in the range 0-999999.

InterruptedException - if another thread has interrupted the current thread. The

interrupted status of the current thread is cleared when this exception is thrown.

join

public final void join()

throws InterruptedException

Waits for this thread to die.

Throws: InterruptedException - if another thread has interrupted the current thread. The

interrupted status of the current thread is cleared when this exception is thrown.

dumpStack

public static void dumpStack()

Prints a stack trace of the current thread. This method is used only for debugging.

See Also: Throwable.printStackTrace()

setDaemon

public final void setDaemon(boolean on)

Marks this thread as either a daemon thread or a user thread. The Java Virtual

Machine exits when the only threads running are all daemon threads.

This method must be called before the thread is started.

This method first calls the checkAccess method of this thread with no arguments.

This may result in throwing a SecurityException (in the current thread).

Parameters:

on - if true, marks this thread as a daemon thread.

Throws: IllegalThreadStateException - if this thread is active.

SecurityException - if the current thread cannot modify this thread.

See Also:

isDaemon(), checkAccess()

isDaemon

public final boolean isDaemon()

Tests if this thread is a daemon thread.

Returns:

true if this thread is a daemon thread; false otherwise.

See Also: setDaemon(boolean)

checkAccess

public final void checkAccess()

Determines if the currently running thread has permission to modify this thread.

If there is a security manager, its checkAccess method is called with this thread as

its argument. This may result in throwing a SecurityException.

Note: This method was mistakenly non-final in JDK 1.1. It has been made final in

the Java 2 Platform.

Throws: SecurityException - if the current thread is not allowed to access this thread.

See Also: SecurityManager.checkAccess(java.lang.Thread)

toString

public String toString()

Returns a string representation of this thread, including the thread's name, priority,

and thread group.

Overrides: toString in class Object

Returns: a string representation of this thread.

getContextClassLoader

public ClassLoader getContextClassLoader()

Returns the context ClassLoader for this Thread. The context ClassLoader is

provided by the creator of the thread for use by code running in this thread when

loading classes and resources. If not set, the default is the ClassLoader context of

the parent Thread. The context ClassLoader of the primordial thread is typically set

to the class loader used to load the application.

First, if there is a security manager, and the caller's class loader is not null and the

caller's class loader is not the same as or an ancestor of the context class loader for

the thread whose context class loader is being requested, then the security manager's

checkPermission method is called with a

RuntimePermission("getClassLoader") permission to see if it's ok to get the

context ClassLoader..

Returns: the context ClassLoader for this Thread

Throws:

SecurityException - if a security manager exists and its checkPermission method

doesn't allow getting the context ClassLoader.

Since: 1.2

See Also:

setContextClassLoader(java.lang.ClassLoader),

SecurityManager.checkPermission(java.security.Permission), RuntimePermission

setContextClassLoader

public void setContextClassLoader(ClassLoader cl)

Sets the context ClassLoader for this Thread. The context ClassLoader can be set

when a thread is created, and allows the creator of the thread to provide the

appropriate class loader to code running in the thread when loading classes and

resources.

First, if there is a security manager, its checkPermission method is called with a

RuntimePermission("setContextClassLoader") permission to see if it's ok to

set the context ClassLoader..

Parameters:

cl - the context ClassLoader for this Thread

Throws: SecurityException - if the current thread cannot set the context ClassLoader.

Since: 1.2

See Also:

getContextClassLoader(),

SecurityManager.checkPermission(java.security.Permission), RuntimePermission

holdsLock

public static boolean holdsLock(Object obj)

Returns true if and only if the current thread holds the monitor lock on the

specified object.

This method is designed to allow a program to assert that the current thread already

holds a specified lock:

assert Thread.holdsLock(obj);

Parameters:

obj - the object on which to test lock ownership

Returns:

true if the current thread holds the monitor lock on the specified object.

Throws:

NullPointerException - if obj is null

Since: 1.4

FLUJO EN PROGRAMAS

Programas de flujo único

Un programa de flujo único o mono-hilvanado (single-thread) utiliza un único flujo de

control (thread) para controlar su ejecución. Muchos programas no necesitan la potencia o

utilidad de múltiples flujos de control. Sin necesidad de especificar explícitamente que se

quiere un único flujo de control, muchos de los applets y aplicaciones son de flujo único.

Por ejemplo, en nuestra aplicación estándar de saludo:

public class HolaMundo {

static public void main( String args[] ) {

System.out.println( "Hola Mundo!" );

}

}

Aquí, cuando se llama a main(), la aplicación imprime el mensaje y termina. Esto ocurre

dentro de un único thread.

Programas de flujo múltiple

En nuestra aplicación de saludo, no vemos el thread que ejecuta nuestro programa. Sin

embargo, Java posibilita la creación y control de threads explícitamente. La utilización de

threads en Java, permite una enorme flexibilidad a los programadores a la hora de

plantearse el desarrollo de aplicaciones. La simplicidad para crear, configurar y ejecutar

threads, permite que se puedan implementar muy poderosas y portables

aplicaciones/applets que no se puede con otros lenguajes de tercera generación. En un

lenguaje orientado a Internet como es Java, esta herramienta es vital.

Si se ha utilizado un navegador con soporte Java, ya se habrá visto el uso de múltiples

threads en Java. Habrá observado que dos applet se pueden ejecutar al mismo tiempo, o que

puede desplazar la página del navegador mientras el applet continúa ejecutándose. Esto no

significa que el applet utilice múltiples threads, sino que el navegador es multithreaded.

Las aplicaciones (y applets) multithreaded utilizan muchos contextos de ejecución para

cumplir su trabajo. Hacen uso del hecho de que muchas tareas contienen subtareas distintas

e independientes. Se puede utilizar un thread para cada subtarea.

Mientras que los programas de flujo único pueden realizar su tarea ejecutando las subtareas

secuencialmente, un programa multithreaded permite que cada thread comience y termine

tan pronto como sea posible. Este comportamiento presenta una mejor respuesta a la

entrada en tiempo real.

Vamos a modificar nuestro programa de saludo creando tres threads individuales, que

imprimen cada uno de ellos su propio mensaje de saludo, MultiHola.java:

// Definimos unos sencillos threads. Se detendrán un rato

// antes de imprimir sus nombres y retardos

class TestTh extends Thread {

private String nombre;

private int retardo;

// Constructor para almacenar nuestro nombre

// y el retardo

public TestTh( String s,int d ) {

nombre = s;

retardo = d;

}

// El metodo run() es similar al main(), pero para

// threads. Cuando run() termina el thread muere

public void run() {

// Retasamos la ejecución el tiempo especificado

try {

sleep( retardo );

} catch( InterruptedException e ) {

;

}

// Ahora imprimimos el nombre

System.out.println( "Hola Mundo! "+nombre+" "+retardo );

}

}

public class MultiHola {

public static void main( String args[] ) {

TestTh t1,t2,t3;

// Creamos los threads

t1 = new TestTh( "Thread 1",(int)(Math.random()*2000) );

t2 = new TestTh( "Thread 2",(int)(Math.random()*2000) );

t3 = new TestTh( "Thread 3",(int)(Math.random()*2000) );

// Arrancamos los threads

t1.start();

t2.start();

t3.start();

}

}

Y ya mßs como espectáculo que otra cosa, aunque también podemos tomarlo por el lado

ilustrativo, vemos a continuación la elecución del applet Figuras.java que muestra un

montón de círculos, cada uno de ellos ejecutándose en un thread diferente y con distinta

prioridad cada uno de ellos. La clase Circulo es la que se utiliza para lanzarla todas las

veces que se quiere, de tal forma que cada uno de los círculos presentes en el applet son

instancias de la misma clase Circulo

CREACION Y CONTROL DE THREADS

Creación de un Thread

Hay dos modos de conseguir threads en Java. Una es implementando la interface

Runnable, la otra es extender la clase Thread.

La implementación de la interface Runnable es la forma habitual de crear threads. Las

interfaces proporcionan al programador una forma de agrupar el trabajo de infraestructura

de una clase. Se utilizan para diseñar los requerimientos comunes al conjunto de clases a

implementar. La interface define el trabajo y la clase, o clases, que implementan la

interface realizan ese trabajo. Los diferentes grupos de clases que implementen la interface

tendrán que seguir las mismas reglas de funcionamiento.

Hay una cuantas diferencias entre interface y clase. Primero, una interface solamente puede

contener métodos abstractos y/o variables estáticas y finales (constantes). Las clases, por

otro lado, pueden implementar métodos y contener variables que no sean constantes.

Segundo, una interface no puede implementar cualquier método. Una clase que implemente

una interface debe implementar todos los métodos definidos en esa interface. Una interface

tiene la posibilidad de poder extenderse de otras interfaces y, al contrario que las clases,

puede extenderse de múltiples interfaces. Además, una interface no puede ser instanciada

con el operador new; por ejemplo, la siguiente sentencia no está permitida:

Runnable a = new Runnable(); // No se permite

El primer método de crear un thread es simplemente extender la clase Thread:

class MiThread extends Thread {

public void run() {

. . .

}

El ejemplo anterior crea una nueva clase MiThread que extiende la clase Thread y

sobrecarga el método Thread.run() por su propia implementación. El método run() es

donde se realizará todo el trabajo de la clase. Extendiendo la clase Thread, se pueden

heredar los métodos y variables de la clase padre. En este caso, solamente se puede

extender o derivar una vez de la clase padre. Esta limitación de Java puede ser superada a

través de la implementación de Runnable:

public class MiThread implements Runnable {

Thread t;

public void run() {

// Ejecución del thread una vez creado

}

}

En este caso necesitamos crear una instancia de Thread antes de que el sistema pueda

ejecutar el proceso como un thread. Además, el método abstracto run() está definido en la

interface Runnable tiene que ser implementado. La única diferencia entre los dos métodos

es que este último es mucho más flexible. En el ejemplo anterior, todavía tenemos

oportunidad de extender la clase MiThread, si fuese necesario. La mayoría de las clases

creadas que necesiten ejecutarse como un thread , implementarán la interface Runnable, ya

que probablemente extenderán alguna de su funcionalidad a otras clases.

No pensar que la interface Runnable está haciendo alguna cosa cuando la tarea se está

ejecutando. Solamente contiene métodos abstractos, con lo cual es una clase para dar idea

sobre el diseño de la clase Thread. De hecho, si vemos los fuentes de Java, podremos

comprobar que solamente contiene un método abstracto:

package java.lang;

public interface Runnable {

public abstract void run() ;

}

Y esto es todo lo que hay sobre la interface Runnable. Como se ve, una interface sólo

proporciona un diseño para las clases que vayan a ser implementadas. En el caso de

Runnable, fuerza a la definición del método run(), por lo tanto, la mayor parte del trabajo se

hace en la clase Thread. Un vistazo un poco más profundo a la definición de la clase

Thread nos da idea de lo que realmente está pasando:

public class Thread implements Runnable {

...

public void run() {

if( tarea != null )

tarea.run() ;

}

}

...

}

De este trocito de código se desprende que la clase Thread también implemente la

interface Runnable. tarea.run() se asegura de que la clase con que trabaja (la clase que va a

ejecutarse como un thread) no sea nula y ejecuta el método run() de esa clase. Cuando esto

suceda, el método run() de la clase hará que corra como un thread.

Arranque de un Thread

Las aplicaciones ejecutan main() tras arrancar. Esta es la razón de que main() sea el lugar

natural para crear y arrancar otros threads. La línea de código:

t1 = new TestTh( "Thread 1",(int)(Math.random()*2000) );

crea un nuevo thread. Los dos argumentos pasados representan el nombre del thread y el

tiempo que queremos que espere antes de imprimir el mensaje.

Al tener control directo sobre los threads, tenemos que arrancarlos explícitamente. En

nuestro ejemplo con:

t1.start();

start(), en realidad es un método oculto en el thread que llama al método run().

Manipulación de un Thread

Si todo fue bien en la creación del thread, t1 debería contener un thread válido, que

controlaremos en el método run().

Una vez dentro de run(), podemos comenzar las sentencias de ejecución como en otros

programas. run() sirve como rutina main() para los threads; cuando run() termina, también

lo hace el thread. Todo lo que queramos que haga el thread ha de estar dentro de run(), por

eso cuando decimos que un método es Runnable, nos obliga a escribir un método run().

En este ejemplo, intentamos inmediatamente esperar durante una cantidad de tiempo

aleatoria (pasada a través del constructor):

sleep( retardo );

El método sleep() simplemente le dice al thread que duerma durante los milisegundos

especificados. Se debería utilizar sleep() cuando se pretenda retrasar la ejecución del thread.

sleep() no consume recursos del sistema mientras el thread duerme. De esta forma otros

threads pueden seguir funcionando. Una vez hecho el retardo, se imprime el mensaje "Hola

Mundo!" con el nombre del thread y el retardo.

Suspensión de un Thread

Puede resultar útil suspender la ejecución de un thread sin marcar un límite de tiempo. Si,

por ejemplo, está construyendo un applet con un thread de animación, querrá permitir al

usuario la opción de detener la animación hasta que quiera continuar. No se trata de

terminar la animación, sino desactivarla. Para este tipo de control de thread se puede

utilizar el método suspend().

t1.suspend();

Este método no detiene la ejecución permanentemente. El thread es suspendido

indefinidamente y para volver a activarlo de nuevo necesitamos realizar una invocación al

método resume():

t1.resume();

Parada de un Thread

El último elemento de control que se necesita sobre threads es el método stop(). Se utiliza

para terminar la ejecución de un thread:

t1.stop();

Esta llamada no destruye el thread, sino que detiene su ejecución. La ejecución no se puede

reanudar ya con t1.start(). Cuando se desasignen las variables que se usan en el thread, el

objeto thread (creado con new) quedará marcado para eliminarlo y el garbage collector se

encargará de liberar la memoria que utilizaba.

En nuestro ejemplo, no necesitamos detener explícitamente el thread. Simplemente se le

deja terminar. Los programas más complejos necesitarán un control sobre cada uno de los

threads que lancen, el método stop() puede utilizarse en esas situaciones.

Si se necesita, se puede comprobar si un thread está vivo o no; considerando vivo un thread

que ha comenzado y no ha sido detenido.

t1.isAlive();

Este método devolverá true en caso de que el thread t1 esté vivo, es decir, ya se haya

llamado a su método run() y no haya sido parado con un stop() ni haya terminado el

método run() en su ejecución.

ARRANCAR Y PARAR THREADS

Ahora que ya hemos visto por encima como se arrancan, paran y manipulan threads, vamos

a mostrar un ejemplo un poco más gráfico, se trata de un contador, cuyo código

(App1Thread.java) es el siguiente:

import java.awt.*;

import java.applet.Applet;

public class App1Thread extends Applet implements Runnable {

Thread t;

int contador;

public void init() {

contador = 0;

t = new Thread( this );

t.start();

}

public void run() {

while( true )

{

contador++;

repaint();

try {

t.sleep( 10 );

} catch( InterruptedException e ) {

;

};

}

}

public boolean mouseDown( Event evt,int x,int y ) {

t.stop();

return( true );

}

public void paint( Graphics g ) {

g.drawString( Integer.toString( contador ),10,10 );

System.out.println( "Contador = "+contador );

}

public void stop() {

t.stop();

}

}

Este applet arranca un contador en 0 y lo incrementa, presentando su salida tanto en la

pantalla gráfica como en la consola. Una primera ojeada al código puede dar la impresión

de que el programa empezará a contar y presentará cada número, pero no es así. Una

revisión más profunda del flujo de ejecución del applet, nos revelará su verdadera

identidad.

En este caso, la clase App1Thread está forzada a implementar Runnable sobre la clase

Applet que extiende. Como en todos los applets, el método init() es el primero que se

ejecuta. En init(), la variable contador se inicializa a cero y se crea una nueva instancia de

la clase Thread. Pasándole this al constructor de Thread, el nuevo thread ya conocerá al

objeto que va a correr. En este caso this es una referencia a App1Thread. Después de que

hayamos creado el thread, necesitamos arrancarlo. La llamada a start(), llamará a su vez al

método run() de nuestra clase, es decir, a App1Thread.run(). La llamada a start() retornará

con éxito y el thread comenzará a ejecutarse en ese instante. Observar que el método run()

es un bucle infinito. Es infinito porque una vez que se sale de él, la ejecución del thread se

detiene. En este método se incrementará la variable contador, se duerme 10 milisegundos

y envía una petición de refresco del nuevo valor al applet.

Es muy importante dormirse en algún lugar del thread, porque sino, el thread consumirá

todo el tiempo de la CPU para su proceso y no permitirá que entren otros métodos de otros

threads a ejecutarse. Otra forma de detener la ejecución del thread es hacer una llamada al

método stop(). En el contador, el thread se detiene cuando se pulsa el ratón mientras el

cursor se encuentre sobre el applet. Dependiendo de la velocidad del ordenador, se

presentarán los números consecutivos o no, porque el incremento de la variable contador

es independiente del refresco en pantalla. El applet no se refresca a cada petición que se le

hace, sino que el sistema operativo encolará las peticiones y las que sean sucesivas las

convertirán en un único refresco. Así, mientras los refescos se van encolando, la variable

contador se estará todavía incrementando, pero no se visualiza en pantalla.

SUSPENDER Y REANUDAR THREADS

Una vez que se para un thread, ya no se puede rearrancar con el comando start(), debido a

que stop() concluirá la ejecución del thread. Por ello, en ver de parar el thread, lo que

podemos hacer es dormirlo, llamando al método sleep(). El thread estará suspendido un

cierto tiempo y luego reanudará su ejecución cuando el límite fijado se alcance. Pero esto

no es útil cuando se necesite que el thread reanude su ejecución ante la presencia de ciertos

eventos. En estos casos, el método suspend() permite que cese la ejecución del thread y el

método resume() permite que un método suspendido reanude su ejecución. En la siguiente

versión de nuestra clase contador, App2Thread.java, modificamos el applet para que

utilice los métodos suspend() y resume():

public class App2Thread extends Applet implements Runnable {

Thread t;

int contador;

boolean suspendido;

...

public boolean mouseDown( Event evt,int x,int y ) {

if( suspendido )

t.resume();

else

t.suspend();

suspendido = !suspendido;

return( true );

}

...

Para controlar el estado del applet, hemos introducido la variable suspendido. Diferenciar

los distintos estados de ejecución del applet es importante porque algunos métodos pueden

generar excepciones si se llaman desde un estado erróneo. Por ejemplo, si el applet ha sido

arrancado y se detiene con stop(), si se intenta ejecutar el método start(), se generará una

excepción IllegalThreadStateException.

ESTADOS DE UN THREAD

Durante el ciclo de vida de un thread, éste se puede encontrar en diferentes estados. La

figura siguiente muestra estos estados y los métodos que provocan el paso de un estado a

otro. Este diagrama no es una máquina de estados finita, pero es lo que más se aproxima al

funcionamiento real de un thread .

Nuevo Thread

La siguiente sentencia crea un nuevo thread pero no lo arranca, lo deja en el estado de

"Nuevo Thread":

Thread MiThread = new MiClaseThread();

Cuando un thread está en este estado, es simplemente un objeto Thread vacío. El sistema no

ha destinado ningún recurso para él. Desde este estado solamente puede arrancarse

llamando al método start(), o detenerse definitivamente, llamando al método stop(); la

llamada a cualquier otro método carece de sentido y lo único que provocará será la

generación de una excepción de tipo IllegalThreadStateException.

Ejecutable

Ahora veamos las dos línea de código que se presentan a continuación:

Thread MiThread = new MiClaseThread();

MiThread.start();

La llamada al método start() creará los recursos del sistema necesarios para que el thread

puede ejecutarse, lo incorpora a la lista de procesos disponibles para ejecución del sistema

y llama al método run() del thread. En este momento nos encontramos en el estado

"Ejecutable" del diagrama. Y este estado es Ejecutable y no En Ejecución, porque cuando

el thread está aquí no esta corriendo. Muchos ordenadores tienen solamente un procesador

lo que hace imposible que todos los threads estén corriendo al mismo tiempo. Java

implementa un tipo de scheduling o lista de procesos, que permite que el procesador sea

compartido entre todos los procesos o threads que se encuentran en la lista. Sin embargo,

para nuestros propósitos, y en la mayoría de los casos, se puede considerar que este estado

es realmente un estado "En Ejecución", porque la impresión que produce ante nosotros es

que todos los procesos se ejecutan al mismo tiempo.

Cuando el thread se encuentra en este estado, todas las instrucciones de código que se

encuentren dentro del bloque declarado para el método run(), se ejecutarán

secuencialmente.

Parado

El thread entra en estado "Parado" cuando alguien llama al método suspend(), cuando se

llama al método sleep(), cuando el thread está bloqueado en un proceso de entrada/salida o

cuando el thread utiliza su método wait() para esperar a que se cumpla una determinada

condición. Cuando ocurra cualquiera de las cuatro cosas anteriores, el thread estará Parado.

Por ejemplo, en el trozo de código siguiente:

Thread MiThread = new MiClaseThread();

MiThread.start();

try {

MiThread.sleep( 10000 );

} catch( InterruptedException e ) {

;

}

la línea de código que llama al método sleep():

MiThread.sleep( 10000 );

hace que el thread se duerma durante 10 segundos. Durante ese tiempo, incluso aunque el

procesador estuviese totalmente libre, MiThread no correría. Después de esos 10 segundos.

MiThread volvería a estar en estado "Ejecutable" y ahora sí que el procesador podría

hacerle caso cuando se encuentre disponible.

Para cada una de los cuatro modos de entrada en estado Parado, hay una forma específica

de volver a estado Ejecutable. Cada forma de recuperar ese estado es exclusiva; por

ejemplo, si el thread ha sido puesto a dormir, una vez transcurridos los milisegundos que se

especifiquen, él solo se despierta y vuelve a estar en estado Ejecutable. Llamar al método

resume() mientras esté el thread durmiendo no serviría para nada.

Los métodos de recuperación del estado Ejecutable, en función de la forma de llegar al

estado Parado del thread, son los siguientes:

• Si un thread está dormido, pasado el lapso de tiempo

• Si un thread está suspendido, luego de una llamada al método resume()

• Si un thread está bloqueado en una entrada/salida, una vez que el comando E/S

concluya su ejecución

• Si un thread está esperando por una condición, cada vez que la variable que controla

esa condición varíe debe llamarse a notify() o notifyAll()

Muerto

Un thread se puede morir de dos formas: por causas naturales o porque lo maten (con

stop()). Un thread muere normalmente cuando concluye de forma habitual su método run().

Por ejemplo, en el siguiente trozo de código, el bucle while es un bucle finito -realiza la

iteración 20 veces y termina-:

public void run() {

int i=0;

while( i < 20 )

{

i++;

System.out.println( "i = "+i );

}

}

Un thread que contenga a este método run(), morirá naturalmente después de que se

complete el bucle y run() concluya.

También se puede matar en cualquier momento un thread, invocando a su método stop().

En el trozo de código siguiente:

Thread MiThread = new MiClaseThread();

MiThread.start();

try {

MiThread.sleep( 10000 );

} catch( InterruptedException e ) {

;

}

MiThread.stop();

se crea y arranca el thread MiThread, lo dormimos durante 10 segundos y en el momento

de despertarse, la llamada a su método stop(), lo mata.

El método stop() envía un objeto ThreadDeath al thread que quiere detener. Así, cuando un

thread es parado de este modo, muere asíncronamente. El thread morirá en el momento en

que reciba la excepción ThreadDeath.

Los applets utilizarán el método stop() para matar a todos sus threads cuando el navegador

con soporte Java en el que se están ejecutando le indica al applet que se detengan, por

ejemplo, cuando se minimiza la ventana del navegador o cuando se cambia de página.

El método isAlive()

La interface de programación de la clase Thread incluye el método isAlive(), que devuelve

true si el thread ha sido arrancado (con start()) y no ha sido detenido (con stop()). Por ello,

si el método isAlive() devuelve false, sabemos que estamos ante un "Nuevo Thread" o ante

un thread "Muerto". Si nos devuelve true, sabemos que el thread se encuentra en estado

"Ejecutable" o "Parado". No se puede diferenciar entre "Nuevo Thread" y "Muerto", ni

entre un thread "Ejecutable" o "Parado".

SCHEDULING

Java tiene un Scheduler, una lista de procesos, que monitoriza todos los threads que se

están ejecutando en todos los programas y decide cuales deben ejecutarse y cuales deben

encontrarse preparados para su ejecución. Hay dos características de los threads que el

scheduler identifica en este proceso de decisión. Una, la más importante, es la prioridad del

thread; la otra, es el indicador de demonio. La regla básica del scheduler es que si

solamente hay threads demonio ejecutándose, la Máquina Virtual Java (JVM) concluirá.

Los nuevos threads heredan la prioridad y el indicador de demonio de los threads que los

han creado. El scheduler determina qué threads deberán ejecutarse comprobando la

prioridad de todos los threads, aquellos con prioridad más alta dispondrán del procesador

antes de los que tienen prioridad más baja.

El scheduler puede seguir dos patrones, preemptivo y no-preemptivo. Los schedulers

preemtivos proporcionan un segmento de tiempo a todos los threads que están corriendo en

el sistema. El scheduler decide cual será el siguiente thread a ejecutarse y llama a resume()

para darle vida durante un período fijo de tiempo. Cuando el thread ha estado en ejecución

ese período de tiempo, se llama a suspend() y el siguiente thread en la lista de procesos será

relanzado (resume()). Los schedulers no-preemtivos deciden que thread debe correr y lo

ejecutan hasta que concluye. El thread tiene control total sobre el sistema mientras esté en

ejecución. El método yield() es la forma en que un thread fuerza al scheduler a comenzar la

ejecución de otro thread que esté esperando. Dependiendo del sistema en que esté corriendo

Java, el scheduler será preemtivo o no-preemptivo.

En el siguiente ejemplo, SchThread.java, mostramos la ejecución de dos threads con

diferentes prioridades. Un thread se ejecuta a prioridad más baja que el otro. Los threads

incrementarán sus contadores hasta que el thread que tiene prioridad más alta alcance al

contador que corresponde a la tarea con ejecución más lenta.

PRIORIDADES, DEMONIOS...

Prioridades

El scheduler determina el thread que debe ejecutarse en función de la prioridad asignada a

cada uno de ellos. El rango de prioridades oscila entre 1 y 10. La prioridad por defecto de

un thread es Thread.NORM_PRIORITY, que tiene asignado un valor de 5. Hay otras dos

variables estáticas disponibles, que son Thread.MIN_PRORITY, fijada a 1, y

Thread.MAX_PRIORITY, aque tiene un valor de 10. El método getPriority() puede

utilizarse para conocer el valor actual de la prioridad de un thread.

Threads Demonio

Los threads demonio también se llaman servicios, porque se ejecutan, normalmente, con

prioridad baja y proporcionan un servicio básico a un programa o programas cuando la

actividad de la máquina es reducida. Un ejemplo de thread demonio que está ejecutándose

continuamente es el recolector de basura (garbage collector). Este thread, proporcionado

por la Máquina Virtual Java, comprueba las variables de los programas a las que no se

accede nunca y libera estos recursos, devolviéndolos al sistema. Un thread puede fijar su

indicador de demonio pasando un valor true al método setDaemon(). Si se pasa false a este

método, el thread será devuelto por el sistema como un thread de usuario. No obstante, esto

último debe realizarse antes de que se arranque el thread (start()).

Diferencia de threads con fork()

fork() en Unix crea un proceso hijo que tiene su propia copia de datos y código del padre.

Esto funciona correctamente si estamos sobrados de memoria y disponemos de una CPU

poderosa, y siempre que mantengamos el número de procesos hijos dentro de un límite

manejable, porque se hace un uso intensivo de los recursos del sistema. Los applets Java no

pueden lanzar ningún proceso en el cliente, porque eso sería una fuente de inseguridad y no

está permitido. Las aplicaciones y los applets deben utilizar threads.

La multi-tarea pre-emptiva tiene sus problemas. Un thread puede interrumpir a otro en

cualquier momento, de ahí lo de pre-emptive. Imaginarse lo que pasaría si un thread está

escribiendo en un array, mientras otro thread lo interrumpe y comienza a escribir en el

mismo array. Los lenguajes como C y C++ necesitan de las funciones lock() y unlock() para

antes y después de leer o escribir datos. Java también funciona de este modo, pero oculta el

bloqueo de datos bajo la sentencia synchronized:

synchronized int MiMetodo();

Otro área en que los threads son muy útiles es en los interfaces de usuario. Permiten

incrementar la respuesta del ordenador ante el usuario cuando se encuentra realizando

complicados cálculos y no puede atender a la entrada de usuario. Estos cálculos se pueden

realizar en segundo plano, o realizar varios en primer plano (música y animaciones) sin que

se dé apariencia de pérdida de rendimiento.

Este es un ejemplo de un applet, Animacion.java, que crea un thread de animación que nos

presenta el globo terráqueo en rotación. Aquí podemos ver que estamos creando un thread de

Fuentes:

a) http://sunsite.dcc.uchile.cl/java/docs/JavaTut/Cap7/thread.html

b) http://www.reloco.com.ar/prog/java/threads.html

c) http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Thread.html

COMUNICACION ENTRE THREADS

Otra clave para el éxito y la ventaja de la utilización de múltiples threads en una aplicación,

o aplicación multithreaded, es que pueden comunicarse entre sí. Se pueden diseñar threads

para utilizar objetos comunes, que cada thread puede manipular independientemente de los

otros threads.

El ejemplo clásico de comunicación de threads es un modelo productor/consumidor. Un

thread produce una salida, que otro thread usa (consume), sea lo que sea esa salida. Vamos

entonces a crear un productor, que será un thread que irá sacando caracteres por su salida;

crearemos también un consumidor que ira recogiendo los caracteres que vaya sacando el

productor y un monitor que controlará el proceso de sincronización entre los threads.

Funcionará como una tubería, insertando el productor caracteres en un extremos y

leyéndolos el consumidor en el otro, con el monitor siendo la propia tubería.

Productor

El productor extenderá la clase Thread, y su código es el siguiente:

class Productor extends Thread {

private Tuberia tuberia;

private String alfabeto = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

public Productor( Tuberia t ) {

// Mantiene una copia propia del objeto compartido

tuberia = t;

}

public void run() {

char c;

// Mete 10 letras en la tubería

for( int i=0; i < 10; i++ )

{

c = alfabeto.charAt( (int)(Math.random()*26 ) );

tuberia.lanzar( c );

// Imprime un registro con lo añadido

System.out.println( "Lanzado "+c+" a la tuberia." );

// Espera un poco antes de añadir más letras

try {

sleep( (int)(Math.random() * 100 ) );

} catch( InterruptedException e ) {

;

}

}

}

}

Notar que creamos una instancia de la clase Tuberia, y que se utiliza el método

tuberia.lanzar() para que se vaya construyendo la tubería, en principio de 10 caracteres.

Consumidor

Veamos ahora el código del consumidor, que también extenderá la clase Thread:

class Consumidor extends Thread {

private Tuberia tuberia;

public Consumidor( Tuberia t ) {

// Mantiene una copia propia del objeto compartido

tuberia = t;

}

public void run() {

char c;

// Consume 10 letras de la tubería

for( int i=0; i < 10; i++ )

{

c = tuberia.recoger();

// Imprime las letras retiradas

System.out.println( "Recogido el caracter "+c );

// Espera un poco antes de coger más letras

try {

sleep( (int)(Math.random() * 2000 ) );

} catch( InterruptedException e ) {

;

}

}

}

}

En este caso, como en el del productor, contamos con un método en la clase Tuberia,

tuberia.recoger(), para manejar la información.

Monitor

Una vez vistos el productor de la información y el consumidor, nos queda por ver qué es lo

que hace la clase Tuberia.

Lo que realiza la clase Tuberia, es una función de supervisión de las transacciones entre

los dos threads, el productor y el consumidor. Los monitores, en general, son piezas muy

importantes de las aplicaciones multithreaded, porque mantienen el flujo de comunicación

entre los threads.

class Tuberia {

private char buffer[] = new char[6];

private int siguiente = 0;

// Flags para saber el estado del buffer

private boolean estaLlena = false;

private boolean estaVacia = true;

// Método para retirar letras del buffer

public synchronized char recoger() {

// No se puede consumir si el buffer está vacío

while( estaVacia == true )

{

try {

wait(); // Se sale cuando estaVacia cambia a false

} catch( InterruptedException e ) {

;

}

}

// Decrementa la cuenta, ya que va a consumir una letra

siguiente--;

// Comprueba si se retiró la última letra

if( siguiente == 0 )

estaVacia = true;

// El buffer no puede estar lleno, porque acabamos de consumir

estaLlena = false;

notify();

// Devuelve la letra al thread consumidor

return( buffer[siguiente] );

}

// Método para añadir letras al buffer

public synchronized void lanzar( char c ) {

// Espera hasta que haya sitio para otra letra

while( estaLlena == true )

{

try {

wait(); // Se sale cuando estaLlena cambia a false

} catch( InterruptedException e ) {

;

}

}

// Añade una letra en el primer lugar disponible

buffer[siguiente] = c;

// Cambia al siguiente lugar disponible

siguiente++;

// Comprueba si el buffer está lleno

if( siguiente == 6 )

estaLlena = true;

estaVacia = false;

notify();

}

}

En la clase Tuberia vemos dos características importantes: los miembros dato (buffer[])

son privados, y los métodos de acceso (lanzar() y recoger()) son sincronizados.

Aquí vemos que la variable estaVacia es un semáforo, como los de toda la vida. La

naturaleza privada de los datos evita que el productor y el consumidor accedan

directamente a éstos. Si se permitiese el acceso directo de ambos threads a los datos, se

podrían producir problemas; por ejemplo, si el consumidor intenta retirar datos de un buffer

vacío, obtendrá excepciones innecesarias, o se bloqueará el proceso.

Los métodos sincronizados de acceso impiden que los productores y consumidores

corrompan un objeto compartido. Mientras el productor está añadiendo una letra a la

tubería, el consumidor no la puede retirar y viceversa. Esta sincronización es vital para

mantener la integridad de cualquier objeto compartido. No sería lo mismo sincronizar la

clase en vez de los métodos, porque esto significaría que nadie puede acceder a las

variables de la clase en paralelo, mientras que al sincronizar los métodos, sí pueden acceder

a todas las variables que están fuera de los métodos que pertenecen a la clase.

Se pueden sincronizar incluso variables, para realizar alguna acción determinada sobre

ellas, por ejemplo:

sincronized( p ) {

// aquí se colocaría el código

// los threads que estén intentando acceder a p se pararán

// y generarán una InterruptedException

}

El método notify() al final de cada método de acceso avisa a cualquier proceso que esté

esperando por el objeto, entonces el proceso que ha estado esperando intentará acceder de

nuevo al objeto. En el método wait() hacemos que el thread se quede a la espera de que le

llegue un notify(), ya sea enviado por el thread o por el sistema.

Ahora que ya tenemos un productor, un consumidor y un objeto compartido, necesitamos

una aplicación que arranque los threads y que consiga que todos hablen con el mismo

objeto que están compartiendo. Esto es lo que hace el siguiente trozo de código, del fuente

TubTest.java:

class TubTest {

public static void main( String args[] ) {

Tuberia t = new Tuberia();

Productor p = new Productor( t );

Consumidor c = new Consumidor( t );

p.start();

c.start();

}

}

Compilando y ejecutando esta aplicación, podremos observar nuestro modelo el pleno

funcionamiento.

Monitorización del Productor

Los programas productor/consumidor a menudo emplean monitorización remota, que

permite al consumidor observar el thread del productor interaccionando con un usuario o

con otra parte del sistema. Por ejemplo, en una red, un grupo de threads productores

podrían trabajar cada uno en una workstation. Los productores imprimirían documentos,

almacenando una entrada en un registro (log). Un consumidor (o múltiples consumidores)

podría procesar el registro y realizar durante la noche un informe de la actividad de

impresión del día anterior.

Otro ejemplo, a pequeña escala podría ser el uso de varias ventanas en una workstation.

Una ventana se puede usar para la entrada de información (el productor), y otra ventana

reaccionaría a esa información (el consumidor).

Peer, es un observador general del sistema.