Thread en Java
Thread en Java
Thread en Java
subprocesamiento multiple.
Thread
Introducción
El cuerpo humano puede realizar muchas operaciones a la vez. Para las computadoras personales de
escritorio es tarea común compilar un programa, enviar un archivo a una impresora y recibir mensajes
de correo electrónico a través de la red de manera concurrente.
En java el programador especifica que las aplicaciones contienen subprocesos de ejecución, en donde
cada subproceso designa una porción de un programa que pueda ejecutarse concurrentemente con
otros subprocesos. Esta capacidad, llamada subprocesamiento múltiple, ofrece al programador de java
poderosas herramientas que no están disponibles en CyC++ lenguajes en los cuales se basa java. (En
muchas plataformas computacionales los programas de CyC++ pueden realizar el subprocesamiento
múltiple mediante el uso de bibliotecas de código específicas para cada plataforma.)
Programación de subprocesos; es el proceso por el que se da cada subproceso al procesador, para que
el subproceso pueda realizar su tarea. Cada subproceso tiene una prioridad que determina el orden para
programar subprocesos. En algunas plataformas, un subproceso de cierta prioridad se ejecuta hasta
completarlo o hasta que otro de más prioridad necesita usar el procesador, en este caso, los de menor
prioridad deben esperar. En Microsoft Windows, los subprocesos se dividen por tiempo, otorgándose a
cada subproceso una cantidad limitada de tiempo (cuanto de tiempo) para ejecutarse; al expirar su
cuanto de tiempo se espera, mientras otro subproceso usa su cuanto de tiempo. Este proceso ocurre en
forma cíclica (round-robin) así, todos los subprocesos de igual prioridad tienen oportunidad de
ejecutarse. El subproceso original reanuda su ejecución.
Los subprocesos pueden encontrarse en uno de varios estados de subprocesos. Un nuevo subproceso
inicia su ciclo al hacer la transición al estado Nacimiento, permanece así hasta llamar al método start de
clase Tread, haciendo la transición del subproceso al estado Listo (también llamado ejecutable),
entonces el subproceso que llamó a start, el recientemente iniciado y cualquier otro, se ejecutan
concurrentemente. Un subproceso hace la transición del estado Listo a Ejecucion (empieza a ejecutarse)
al recibir un procesador asignado por el SO, se conoce como despachar el subproceso. Cuando el
método run se completa de ejecutarse es cuando un subproceso en ejecución pasa a su estado Muerto.
Cuando un proceso esta muerto y no hay referencias para el objeto del subproceso, el CG puede
eliminar ese objeto.
Un proceso cambia al estado bloqueado cuando intenta realizar una tarea que no puede completarse de
forma inmediata y debe esperar para completarla; por ejemplo cuando un proceso envía una solicitud
de E/S. En este caso el SO bloquea la ejecución del subproceso hasta que pueda completarse dicha
solicitud. En este punto el subproceso cambia al estado Listo para que pueda despacharse de nuevo y
reanudar su ejecución. Un proceso bloqueado no puede usar un procesador aunque esté disponible.
Si un subproceso encuentra un código que no puede ejecutar llama al método wait de Object para
cambiar al estado En espera. Este cambia al estado Listo invocando al método notify(un subproceso) o
notifiAll(para todos los subprocesos).
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.
Creacion de un thread
Hay dos modos de conseguir threads en Java. Una es implementando la interface Runnable, la otra es
extender la clase Thread.
...
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.
Thread t;
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.
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:
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();
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();
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.
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.
Threads daemon
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()).
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.