HILOS Y SEMAFOROS

45
Antecedentes 1. Manejar sentencias de control de flujo en lenguaje de Java. 2. Identificar y relacionar varias clases en la programación orientada a objetos empleando la teoría de diseño de jerarquía de clases 3. Haber utilizado archivos en el almacenamiento de datos 4. Manejar los conceptos de proceso y multitareas Introducción En Java, para lograr la comunicación entre el programa y la información, ya sea de entrada o salida, se hace mediante un flujo, que funge como el intermediario entre el programa y los datos. En la práctica de programación orientada a objetos con manejo de excepciones, se observó que para hacer la lectura de datos desde el teclado, se tiene que generar un flujo de entrada, es decir, una operación InputStreamReader; y el parámetro que recibe es el origen de los datos, en ese caso System.in, pero para realizar la lectura de información contenida en archivos o ficheros, se necesita generar un objeto de tipo File, igual que como se crea un apuntador FILE en C. File f = new File(nombreArchivo); Una vez creado el apuntador, se necesita generar el flujo de datos desde un archivo, es decir, crear un objeto de tipo FileInputStream, que recibe como parámetro el objeto de tipo File. FileInputStream fis = new FileInputStream(f); Con esto, ya se puede crear el InputStreamReader, que permitirá leer los datos que se tengan en un archivo; y el BufferedReader que hará la lectura de los mismos. InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); Ya con estas cuatro líneas se puede hacer la lectura de un archivo. PRÁCTICA PROGRAMACIÓN ORIENTADA A OBJETOS (ARCHIVOS E HILOS)

Transcript of HILOS Y SEMAFOROS

Page 1: HILOS Y SEMAFOROS

Antecedentes1. Manejar sentencias de control de flujo en lenguaje de Java.2. Identificar y relacionar varias clases en la programación orientada a objetosempleando la teoría de diseño de jerarquía de clases3. Haber utilizado archivos en el almacenamiento de datos4. Manejar los conceptos de proceso y multitareasIntroducciónEn Java, para lograr la comunicación entre el programa y la información, ya sea de entradao salida, se hace mediante un flujo, que funge como el intermediario entre el programa y losdatos.En la práctica de programación orientada a objetos con manejo de excepciones, se observóque para hacer la lectura de datos desde el teclado, se tiene que generar un flujo de entrada,es decir, una operación InputStreamReader; y el parámetro que recibe es el origen de losdatos, en ese caso System.in, pero para realizar la lectura de información contenida enarchivos o ficheros, se necesita generar un objeto de tipo File, igual que como se crea unapuntador FILE en C.File f = new File(nombreArchivo);Una vez creado el apuntador, se necesita generar el flujo de datos desde un archivo, es decir,crear un objeto de tipo FileInputStream, que recibe como parámetro el objeto de tipo File.FileInputStream fis = new FileInputStream(f);Con esto, ya se puede crear el InputStreamReader, que permitirá leer los datos que setengan en un archivo; y el BufferedReader que hará la lectura de los mismos.InputStreamReader isr = new InputStreamReader(fis);BufferedReader br = new BufferedReader(isr);Ya con estas cuatro líneas se puede hacer la lectura de un archivo.

PRÁCTICAPROGRAMACIÓN ORIENTADA A OBJETOS (ARCHIVOS E HILOS)

Elaborar un programa que lea la información contenida en un archivo de texto plano.import java.io.*;public class LeeArchivo{File f;FileInputStream fis;InputStreamReader isr;BufferedReader br;String linea;

Page 2: HILOS Y SEMAFOROS

public static void main(String args[]){try{LeeArchivo la = new LeeArchivo();//Se crea un objeto de tipo Filela.f = new File(args[0]);//Se genera el flujo de entrada desde archivola.fis = new FileInputStream(la.f);//Se crea el flujo de lecturala.isr = new InputStreamReader(la.fis);//Se crea el objeto que permitirá la lecturala.br = new BufferedReader(la.isr);//Se hará la lectura hasta que encuentre una línea nulawhile((la.linea=la.br.readLine())!=null){System.out.println(la.linea);}//Se cierran todos los flujosla.br.close();la.isr.close();la.fis.close();}catch(IOException ioe){System.out.println(ioe.getMessage());}}}Debido a que todos los flujos de entrada y salida generan una excepción de entrada/salida,es necesario agregar una secuencia de try-catch.Escritura en archivosAhora, para hacer la escritura en archivos se debe generar, de igual forma que para lalectura, flujos de entrada o salida. En este caso, en lugar de utilizar FileInputStream,debemos generar un flujo de salida, es decir FileOutputStream.FileOutputStream fos = FileOutputStream(f);

Debido a que ahora la impresión es en un archivo, se deberá utilizar el comando:PrintStream ps = PrintStream(fos);Ejemplo 2Elaborar un programa que escriba en archivo una línea que se escriba desde línea decomando.import java.io.*;public class EscribeArchivo{String cadena;File f;FileOutputStream fos;PrintStream ps;public static void main(String args[]){try{EscribeArchivo ea = new EscribeArchivo();

Page 3: HILOS Y SEMAFOROS

//Se obtiene la cadena que se escribirá en el archivoea.cadena = args[0];//Se crea un objeto de tipo Fileea.f = new File(args[1]);//Se genera el flujo de salida hacia el archivoea.fos = new FileOutputStream(ea.f);//Se genera el escritor del flujoea.ps = new PrintStream(ea.fos);//Se escribe la línea dentro del archivoea.ps.println(ea.cadena);//Se cierran flujosea.ps.close();ea.fos.close();}catch(IOException ioe){}catch(ArrayIndexOutOfBoundsException aioobe){System.out.println("ERROR!!!");System.out.println("Sintaxis: java EscribeArchivo\"cadena\" archivo");}}}

Hilos

Cuando se genera un proceso, se crea un hilo primario, que está asociado a un bloque deinstrucciones, un conjunto de registros y una pila. Este hilo, puede crear más hilos si esnecesario; a estos hilos se les denomina “procesos ligeros” ya que comparten ciertosrecursos con el proceso primario que los creó.

PRÁCTICAPROGRAMACIÓN ORIENTADA A OBJETOS (ARCHIVOS E HILOS)

Pero, ¿para qué se necesitan varios hilos? Cuando se hace una aplicación robusta en el quetenga que hacer varias acciones al mismo tiempo, en lugar de crear un proceso completo, esmás conveniente crear un hilo que pueda llevar a cabo las acciones correspondientes de unaforma rápida y eficaz. Por ejemplo, si se tiene un sistema que está en red, para quemúltiples usuarios tengan acceso, si se generara un proceso por cada usuario, saldríabastante caro, hablando de recursos máquina, pero si se genera un hilo, para que haga una

Page 4: HILOS Y SEMAFOROS

consulta, o una actualización de un dato, las acciones se realizan rápidamente.Hay varios estados por los que pasa un hilo, y son:Preparado.- Cuando se genera un hilo nuevo y está listo para ejecutarse, o cuandoha sido desbloqueado.En ejecución.- Cuando está haciendo uso de los recurso de la CPU.Bloqueado.- Cuando está en espera de que se elimine el bloqueo.o Dormido.- Cuando se bloquea por un cierto tiempo, después del cualdespierta, y pasa a preparado.o En espera.- Cuando está esperando a que ocurra algo, recibe un mensaje ypasa a preparado.Muerto.- Cuando ha terminado de ejecutarse.En Java, cuando se trabaja con hilos, se debe indicar que la clase con la que se estátrabajando es una subclase de la clase Thread, es decir, que se heredarán todas laspropiedades que contiene la clase Thread.Ejemplo 3Elabore un programa que genere 6 hilos.public class GeneraHilo extends Thread{//Rutina que se ejecuta cuando se le da start() a un hilopublic void run(){System.out.println("Esta es una instancia de nuestra clase: "+this.getName());}public static void main(String args[]){//Se generan 6 hilosGeneraHilo gh1 = new GeneraHilo();GeneraHilo gh2 = new GeneraHilo();GeneraHilo gh3 = new GeneraHilo();GeneraHilo gh4 = new GeneraHilo();GeneraHilo gh5 = new GeneraHilo();GeneraHilo gh6 = new GeneraHilo();//Se les asigna nombregh1.setName("Hilo1");gh2.setName("Hilo2");gh3.setName("Hilo3");gh4.setName("Hilo4");gh5.setName("Hilo5");gh6.setName("Hilo6");

//Se "ejecutan" los hilosgh1.start();gh2.start();gh3.start();gh4.start();gh5.start();gh6.start();System.out.println("Hilo Principal:"

Page 5: HILOS Y SEMAFOROS

+Thread.currentThread().getName());}}

EJERCICIOS PROPUESTOS1. El programa del Ejemplo 1, tiene una falla cuando no se le pasan parámetros,arréglelo utilizando un catch. Además, cuando se le da el nombre de un archivo noexistente aparece la leyenda(El sistema no puede hallar el archivo especificado)Modifique el programa de tal forma que se indique:ERROR!!!Archivo no válido, favor de verificar el nombre del archivo.TIP: Utilice una excepción para la modificación de archivo no válido.2. Modifique el programa del Ejemplo 2 de tal forma que ahora se escriba en archivotodo lo que se escriba mientras está en ejecución el programa.3. Modifique el programa anterior, para que también se pueda agregar información alfinal del archivo que se indique.4. Elabore un programa que lea información de un archivo, y lo escriba en otro.5. Cuando se ejecuta varias veces seguidas el programa del Ejemplo 3, ¿qué pasa?Ahora en el método run(), agregue la instrucciónsleep((long)Math.random()*2500), antes de la impresión del nombre delhilo que se está ejecutando. Haga las modificaciones necesarias para que compile yejecute correctamente el programa. Si se ejecuta varias veces seguidas el programa,¿Ahora, qué sucede?

Page 6: HILOS Y SEMAFOROS

Concepto de Hilos de Ejecución

Para solucionar este problema, los programadores han creado lo que se llaman "hilos de ejecución" (Threads). Su razonamiento es que cada proceso es una colección de uno o más "hilos". Todos los programas tienen al menos un hilo, que es en el caso de los programas de Delphi (y de casi todos los lenguajes de Windows) el mismo hilo que procesa todos los mensajes. Pero cualquier hilo puede crear otro "hilo" con un pedazo de código, y este hilo podrá ejecutar al mismo tiempo que el programa, sin necesidad de interrumpirlo.

TODO: Hacer gráfica de hilos de ejecución.

Esto puede ser bastante complejo; ya que aunque Windows puede protegerlo de accesar memoria que pertenece a otros procesos, no lo va a ayudar si varios hilos tratan de accesar la misma memoria. Así que usted tiene que tener cuidado, cuando accesa las variables de un hilo, de que el hilo no la esté utilizando, porque si no lo hace, los resultados pueden ser impredecibles.

Una de las tareas más complicadas de un desarrollador experto es la depuración de errores causados por hilos de ejecución. Es por esto muy importante que usted comprenda todos los conceptos aquí explicados perfectamente bien. Los errores de hilos de ejecución no siempre son evidentes, y pueden causar que los programas se traben (sin dar mensajes de error) en circunstancias muy diversas y por motivos que muchas veces no son obvios.

Comunicación Entre Hilos

Así que usted puede hacer que varios hilos de ejecución ejecuten simultáneamente en su programa, pero no puede hacer que los hilos accesen memoria de otros hilos al mismo tiempo... ¿Entonces, cómo le hacemos para comunicarnos?

El caso más común donde desearíamos comunicarnos con el hilo de ejecución principal sería para escribir algo en la pantalla (tal vez aumentar el valor de un ProgressBar o escribir en una etiqueta el estado actual de nuestro hilo). Para comunicarnos entre diversos hilos, debemos detener todos los hilos en favor del hilo principal, y decirle a este hilo que actualice la pantalla por nosotros. Este procedimiento se llama "sincronización".

Obviamente, es muy importante evitar que cualquier sincronización tome demasiado tiempo, porque haría a nuestro programa sentirse lento en vez de ayudar.

Implementación de Hilos de Ejecución en Delphi

Page 7: HILOS Y SEMAFOROS

Delphi tiene un objeto específicamente para manejar hilos de ejecución, que es llamado, adecuadamente, TThread. Este objeto es abstracto, lo cual quiere decir que no lo podemos instanciar hasta que lo "heredemos" y creemos nuestro propio TThread. Pero lo que nos da es los primitivos de creación de Hilos y sincronización para que no necesitemos llamar al API de Windows.

¿Cómo le decimos a TThread qué código queremos ejecutar? TThread tiene un procedimiento llamado Execute que usted debe implementar. Es en este procedimiento donde usted puede escribir su código.

A continuación presento una pieza simplificada de código con las porciones indispensables para que un hilo de ejecución funcione:

type

TBoxParser = class(TThread) private protected procedure Execute; override; procedure MostrarProgreso; override; public end;

implementation

procedure TBoxParser.Execute;var i : Integer;begin

for i := 0 to MBox.MessageList.Count -1 do begin FCurrentMessage := 'Reading '+MBox.MessageList.Items[i].MailFrom; if Terminated then exit; // Esto actualiza la forma evitando conflictos entre hilos. Synchronize(MostrarProgreso); end;

end;

procedure TBoxParser.MostrarProgreso;begin

// Esto corre bajo el contexto del hilo principal. mainform.StatusBar1.Panels[0].Text := FCurrentMessage; mainform.ProgressBar1.Max := MboxMax; mainform.ProgressBar1.Position := MBoxCurrent;

end;

Como podrá ver en el código, es responsabilidad de usted checar si el hilo ha sido terminado desde afuera (alguien que llame TThread.Terminate) e interrumpir su proceso.

Page 8: HILOS Y SEMAFOROS

Además, si usted quiere actualizar la interfaz del usuario usted debe llamar Synchronize, ya que todo lo que vive en la interfaz del usuario corre en el contexto del hilo principal.

Prioridad y Señales

Los hilos de ejecución pueden tener varios niveles prioridad. El hilo puede cambiar su propia prioridad, pero debe usted tener en cuenta de que otros hilos en su programa (o el sistema operativo mismo) puede cambiar su prioridad, así que procure no depender en la prioridad.

Ya hemos visto que otros hilos de ejecución pueden pedir que terminemos el programa, y eso lo debemos manejar nosotros mismos. Esta es una de las señales que el programa nos puede enviar, pero también puede suspendernos y continuarnos (Suspend/Resume). Siempre tenga esto en cuenta y programe defensivamente. Por ejemplo, si usted utiliza un recurso en un loop, asegúrese de que el recurso exista dentro del loop. Esa clase de cosas.

Uso de Recursos

Los hilos de ejecución que usted implemente deben estar programados de tal manera que "jueguen limpio" con los recursos. Esto quiere decir verificar que el recurso no esté en uso por otra tarea.

Bases de Datos e Hilos de Ejecución

Uno de los recursos que debemos procurar no accesar en varios hilos al mismo tiempo son las conexiones a bases de datos. El BDE permite multitarea pero no sobre la misma conexión. Esto quiere que usted deberá:

1. Crear un TDatabase y TSession por cada hilo de ejecución 2. Utilizar algún otro método (de programación) que nos permita asegurarnos que la

base de datos nunca se compartirá.

En Delphi es más sencillo crear los hilos y asignarles un nuevo TDatabase y TSession a cada uno.

En el siguiente fragmento de código, estoy creando algunos queries, y al hacer esto estoy pidiendo la sesión y base de datos especificadas en el miembro "Database", que es un TDatabase que yo inventé:

procedure TBoxParser.Execute;var i : Integer;begin

qGente := TQuery.Create(Database.Owner); qEncuestas := TQuery.Create(Database.Owner);

Page 9: HILOS Y SEMAFOROS

qGente.SessionName := Database.SessionName; qGente.DatabaseName := Database.DatabaseName; qEncuestas.SessionName := Database.SessionName; qEncuestas.DatabaseName := Database.DatabaseName; qGente.SQL.Add('SELECT * FROM People WHERE EMail = :Email'); qGente.RequestLive := True; qGente.Params[0].DataType := ftString; qEncuestas.SQL.Add('SELECT * FROM DelphiEncuestas WHERE EMail = :Email'); qEncuestas.Params[0].DataType := ftString; qEncuestas.RequestLive := True;

El código que crea este hilo esta creando la base de datos (cuya variable es DBThread) por mí. Me conecta (para que el diálogo de Login/Password se ejecute desde el hilo principal), y una vez que tenga conectada la base, asigna la variable "Database" del hilo a la TDatabase a la que me conectó. De esta manera puedo estar seguro de que mi base de datos está lista para acceso. Es responsabilidad del hilo principal no utilizar la base hasta que el TThread Termine.

FParseThread := TBoxParser.Create(True); dbThread.Connected := True; TBoxParser(FParseThread).Database := dbThread; FParseThread.Resume;

Nota: El Hilo se está creando con el parámetro "True", que quiere decir que queremos que comience suspendido. Es muy importante dar este parámetro, ya que si no lo hacemos el procedimiento Execute comenzará de inmediato, y no podremos definir los datos que queremos procesar (en este caso, la base de datos que usaremos para conectarnos). Para continuar el proceso una vez que hemos definido todo lo que queramos, podemos llamar "Resume".

Modelos de Diseño con Hilos de ejecución

Cuando diseñamos programas con varios hilos de ejecución que hagan tareas repetitivas, usted debe estar consciente de el manejo los hilos de ejecución en los programas para asegurarse de que su programa no se trabe por uso simultáneo de recursos o demasiados hilos a la vez. Estas consideraciones nos hacen tener que decidir entre dos modelos de diseño:

Hilo-por-Cliente (Thread Per Client)

Page 10: HILOS Y SEMAFOROS

Bajo este modelo, cada cliente que usted genera crea un nuevo hilo. Cuando el cliente se desconecta (o el hilo termina su operación), el hilo se borra. Esto es fácil de programar. Por ejemplo, el evento OnClick de un boton puede crear un TThread y ejecutarlo. Pero el problema es que es fácil trabar el sistema si no tenemos cuidado (el usuario presionando el boton mil veces a la vez). El manual del API de Windows nos recomienda no crear más de 16 hilos de ejecución secundarios para evitar inestabilidad.

Aunque es fácil de escribir, este modelo sólo funciona para unos cuantos hilos, y entre más hilos sean necesarios, más importante es encontrar otra manera.

Colección de Hilos (Thread Pool)

En un Thread Pool, usted tiene un objeto que mantiene una lista de punteros a hilos de ejecución que pueden estar "libres" u "ocupados". Esta colección corre desde el contexto del hilo principal y el hilo principal utiliza la lista para pedir un nuevo hilo. La lista busca entre todos los hilos uno que no esté ocupado. Si encuentra uno, lo proporciona. Si no, crea uno nuevo, lo añade a la lista y lo proporciona, a menos que la lista tenga el Máximo de hilos permitidos, que usted determina (en cuyo caso se crea el error "Servidor demasiado ocupado").

Una vez que el hilo ha terminado la lista se actualiza marcando el hilo como "libre".

Este procedimiento es excelente para los usuarios que necesitan compartir recursos como conexiones de red o conexiones a bases de datos. Estos sistemas son más difíciles de escribir porque debemos crear la colección y mantener la lista, además de asegurarnos de que el código existente nunca llame a los hilos por sí mismo (sino siempre use la lista). Pero nos proporciona dos ventajas:

Primero, nos permite reutilizar hilos que no estén siendo usados, haciendo que nuestro programa pueda dar servicio a más clientes y a la vez reduzca los requerimientos sobre el sistema. Segundo, la complejidad se queda en el módulo de la colección, y mientras usted sea disciplinado en su programación de objetos, cualquier error en su modelo de hilos de ejecución probablemente será un error en la manera de implementar esta lista.

Page 11: HILOS Y SEMAFOROS

3. Programación concurrente multihilo

Introducción:

En sistemas operativos, un hilo de ejecución o subproceso es una característica que permite a una aplicación realizar varias tareas a la vez (concurrentemente). Los distintos hilos de ejecución comparten una serie de recursos tales como el espacio de memoria, los archivos abiertos, situación de autenticación, etc. Esta técnica permite simplificar el diseño de una aplicación que debe llevar a cabo distintas funciones simultáneamente.

Un hilo es básicamente una tarea que puede ser ejecutada en paralelo con otra tarea. Los hilos de ejecución que comparten los mismos recursos, sumados a estos recursos, son en conjunto conocidos como un proceso. El hecho de que los hilos de ejecución de un mismo proceso compartan los recursos hace que cualquiera de estos hilos pueda modificar éstos. Cuando un hilo modifica un dato en la memoria, los otros hilos acceden a ese dato modificado inmediatamente.

Lo que es propio de cada hilo es el contador de programa, la pila de ejecución y el estado de la CPU (incluyendo el valor de los registros).

El proceso sigue en ejecución mientras al menos uno de sus hilos de ejecución siga activo. Cuando el proceso finaliza, todos sus hilos de ejecución también han terminado. Asimismo en el momento en el que todos los hilos de ejecución finalizan, el proceso no existe más y todos sus recursos son liberados.

Algunos lenguajes de programación tienen características de diseño expresamente creadas para permitir a los programadores lidiar con hilos de ejecución (como Java o Delphi). Otros (la mayoría) desconocen la existencia de hilos de ejecución y éstos deben ser creados mediante llamadas de biblioteca especiales que dependen del sistema operativo en el que estos lenguajes están siendo utilizados (como es el caso del C y del C++).

Page 12: HILOS Y SEMAFOROS

3.1 Concepto de Hilo

En sistemas operativos, un hilo de ejecución o subproceso es una característica que permite a una aplicación realizar varias tareas a la vez (concurrentemente). Los distintos hilos de ejecución comparten una serie de recursos tales como el espacio de memoria, los archivos abiertos, situación de autenticación, etc. Esta técnica permite simplificar el diseño de una aplicación que debe llevar a cabo distintas funciones simultáneamente.

Un hilo es básicamente una tarea que puede ser ejecutada en paralelo con otra tarea.

Los hilos de ejecución que comparten los mismos recursos, sumados a estos recursos, son en conjunto conocidos como un proceso. El hecho de que los hilos de ejecución de un mismo proceso compartan los recursos hace que cualquiera de estos hilos pueda modificar éstos. Cuando un hilo modifica un dato en la memoria, los otros hilos acceden a ese dato modificado inmediatamente.

Lo que es propio de cada hilo es el contador de programa, la pila de ejecución y el estado de la CPU (incluyendo el valor de los registros).

El proceso sigue en ejecución mientras al menos uno de sus hilos de ejecución siga activo. Cuando el proceso finaliza, todos sus hilos de ejecución también han terminado. Asimismo en el momento en el que todos los hilos de ejecución finalizan, el proceso no existe más y todos sus recursos son liberados.

Algunos lenguajes de programación tienen características de diseño expresamente creadas para permitir a los programadores lidiar con hilos de ejecución (como Java o Delphi). Otros (la mayoría) desconocen la existencia de hilos de ejecución y éstos deben ser creados mediante llamadas de biblioteca especiales que dependen del sistema operativo en el que estos lenguajes están siendo utilizados (como es el caso del C y del C++).

Un ejemplo de la utilización de hilos es tener un hilo atento a la interfaz gráfica (iconos, botones, ventanas), mientras otro hilo hace una larga operación internamente. De esta manera el programa responde de manera más ágil a la interacción con el usuario. También pueden ser utilizados por una aplicación servidora para dar servicio a múltiples clientes.

Los hilos no pueden ejecutarse ellos solos; requieren la supervisión de un proceso

padre para correr.

Dentro de cada proceso hay varios hilos ejecutándose.

Page 13: HILOS Y SEMAFOROS

Por ejemplo, Word puede tener un hilo en background chequeando

automáticamente la gramática de lo que estoy escribiendo, mientras otro hilo

puede estar salvando automáticamente los cambios del documento en el que

estoy trabajando. Como Word, cada aplicación (proceso) puede correr varios hilos

los cuales están realizando diferentes tareas. Esto significa que los hilos están

siempre asociados con un proceso en particular.

Los hilos a menudo son conocidos o llamados procesos ligeros. Un hilo, en efecto,

es muy similar a un proceso pero con la diferencia de que un hilo siempre corre

dentro del contexto de otro programa. Por el contrario, los procesos mantienen su

propio espacio de direcciones y entorno de operaciones. Los hilos dependen de un

programa padre en lo que se refiere a recursos de ejecución. La siguiente figura

muestra le relación entre hilos y procesos.

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!" );

}

}

Page 14: HILOS Y SEMAFOROS

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

ocurre dentro de un único thread.

La clase Thread

Es la clase que encapsula todo el control necesario sobre los hilos de ejecución

(threads). Hay que distinguir claramente un objeto Thread de un hilo de ejecución

o thread. Esta distinción resulta complicada, aunque se puede simplificar si se

considera al objeto Thread como el panel de control de un hilo de ejecución

(thread). La clase Thread es la única forma de controlar el comportamiento de los

hilos y para ello se sirve de los métodos que se exponen en las secciones

siguientes.

Métodos de Clase

Estos son los métodos estáticos que deben llamarse de manera directa en la clase

Thread.

currentThread()

Este método devuelve el objeto thread que representa al hilo de ejecución que se

está ejecutando actualmente.

yield()

Este método hace que el intérprete cambie de contexto entre el hilo actual y el

siguiente hilo ejecutable disponible. Es una manera de asegurar que nos hilos de

menor prioridad no sufran inanición.

sleep( long )

El método sleep() provoca que el intérprete ponga al hilo en curso a dormir

durante el número de milisegundos que se indiquen en el parámetro de

invocación. Una vez transcurridos esos milisegundos, dicho hilo volverá a estar

disponible para su ejecución. Los relojes asociados a la mayor parte de los

Page 15: HILOS Y SEMAFOROS

intérpretes de Java no serán capaces de obtener precisiones mayores de 10

milisegundos, por mucho que se permita indicar hasta nanosegundos en la

llamada alternativa a este método.

Métodos de Instancia

Aquí no están recogidos todos los métodos de la clase Thread, sino solamente los

más interesantes, porque los demás corresponden a áreas en donde el estándar

de Java no está completo, y puede que se queden obsoletos en la próxima versión

del JDK, por ello, si se desea completar la información que aquí se expone se ha

de recurrir a la documentación del interfaz de programación de aplicación (API) del

JDK.

start()

Este método indica al intérprete de Java que cree un contexto del hilo del sistema

y comience a ejecutarlo. A continuación, el método run() de este hilo será

invocado en el nuevo contexto del hilo. Hay que tener precaución de no llamar al

método start() más de una vez sobre un hilo determinado.

run()

El método run() constituye el cuerpo de un hilo en ejecución. Este es el único

método del interfaz Runnable. Es llamado por el método start() después de que el

hilo apropiado del sistema se haya inicializado. Siempre que el método run()

devuelva el control, el hilo actual se detendrá.

stop()

Este método provoca que el hilo se detenga de manera inmediata. A menudo

constituye una manera brusca de detener un hilo, especialmente si este método se

ejecuta sobre el hilo en curso. En tal caso, la línea inmediatamente posterior a la

llamada al método stop() no llega a ejecutarse jamás, pues el contexto del hilo

muere antes de que stop() devuelva el control. Una forma más elegante de

Page 16: HILOS Y SEMAFOROS

detener un hilo es utilizar alguna variable que ocasione que el método run()

termine de manera ordenada. En realidad, nunca se debería recurrir al uso de este

método.

suspend()

El método suspend() es distinto de stop(). suspend() toma el hilo y provoca que se

detenga su ejecución sin destruir el hilo de sistema subyacente, ni el estado del

hilo anteriormente en ejecución. Si la ejecución de un hilo se suspende, puede

llamarse a resume() sobre el mismo hilo para lograr que vuelva a ejecutarse de

nuevo.

resume()

El método resume() se utiliza para revivir un hilo suspendido. No hay garantías de

que el hilo comience a ejecutarse inmediatamente, ya que puede haber un hilo de

mayor prioridad en ejecución actualmente, pero resume() ocasiona que el hilo

vuelva a ser un candidato a ser ejecutado.

setPriority( int )

El método setPriority() asigna al hilo la prioridad indicada por el valor pasado como

parámetro. Hay bastantes constantes predefinidas para la prioridad, definidas en

la clase Thread, tales como MIN_PRIORITY, NORM_PRIORITY y

MAX_PRIORITY, que toman los valores 1, 5 y 10, respectivamente. Como guía

aproximada de utilización, se puede establecer que la mayor parte de los procesos

a nivel de usuario deberían tomar una prioridad en torno a NORM_PRIORITY. Las

tareas en segundo plano, como una entrada/salida a red o el nuevo dibujo de la

pantalla, deberían tener una prioridad cercana a MIN_PRIORITY. Con las tareas a

las que se fije la máxima prioridad, en torno a MAX_PRIORITY, hay que ser

especialmente cuidadosos, porque si no se hacen llamadas a sleep() o yield(), se

puede provocar que el intérprete Java quede totalmente fuera de control.

getPriority()

Page 17: HILOS Y SEMAFOROS

Este método devuelve la prioridad del hilo de ejecución en curso, que es un valor

comprendido entre uno y diez.

setName( String )

Este método permite identificar al hilo con un nombre menmónico. De esta manera

se facilita la depuración de programas multihilo. El nombre mnemónico aparecerá

en todas las líneas de trazado que se muestran cada vez que el intérprete Java

imprime excepciones no capturadas.

getName()

Este método devuelve el valor actual, de tipo cadena, asignado como nombre al

hilo en ejecución mediante setName().

Funcionalidad de los hilos

Al igual que los procesos, los hilos poseen un estado de ejecución y pueden sincronizarse entre ellos para evitar problemas de compartimiento de recursos. Generalmente, cada hilo tiene una tarea especifica y determinada, como forma de aumentar la eficiencia del uso del procesador.

Estados de un hilo

Los principales estados de los hilos son: Ejecución, Listo y Bloqueado. No tiene sentido asociar estados de suspensión de hilos ya que es un concepto de proceso. En todo caso, si un proceso está expulsado de la memoria principal (ram), todos sus hilos deberán estarlo ya que todos comparten el espacio de direcciones del proceso.

Cambio de estados

Creación: Cuando se crea un proceso se crea un hilo para ese proceso. Luego, este hilo puede crear otros hilos dentro del mismo proceso, proporcionando un puntero de instrucción y los argumentos del nuevo hilo. El hilo tendrá su propio contexto y su propio espacio de la columna, y pasara a la final de los listos.

Bloqueo: Cuando un hilo necesita esperar por un suceso, se bloquea (salvando sus registros de usuario, contador de programa y punteros de pila). Ahora el procesador podrá pasar a ejecutar otro hilo que esté en la final de los Listos mientras el anterior permanece bloqueado.

Page 18: HILOS Y SEMAFOROS

Desbloqueo: Cuando el suceso por el que el hilo se bloqueó se produce, el mismo pasa a la final de los Listos.

Terminación: Cuando un hilo finaliza se liberan tanto su contexto como sus columnas..

Sincronización de hilos

Todos los hilos comparten el mismo espacio de direcciones y otros recursos como pueden ser archivos abiertos. Cualquier modificación de un recurso desde un hilo afecta al entorno del resto de los hilos del mismo proceso.Por lo tanto, es necesario sincronizar la actividad de los distintos hilos para que no interfieran unos con otros o corrompan estructuras de datos.

Una ventaja de la programación multihilo es que los programas operan con mayor velocidad en sistemas de computadores con múltiples CPUs (sistemas multiprocesador o a través de grupo de máquinas) ya que los hilos del programa se prestan verdaderamente para la ejecución concurrente. En tal caso el programador necesita ser cuidadoso para evitar condiciones de carrera (problema que sucede cuando diferentes hilos o procesos alteran datos que otros también están usando), y otros comportamientos no intuitivos. Los hilos generalmente requieren reunirse para procesar los datos en el orden correcto. Es posible que los hilos requieran de operaciones atómicas para impedir que los datos comunes sean cambiados o leídos mientras estén siendo modificados, para lo que usualmente se utilizan los semáforos. El descuido de esto puede generar interbloqueo.

Formas de multihilos

Los sistemas operativos generalmente implementan hilos de dos maneras:

Multihilo apropiativo: permite al sistema operativo determinar cuándo debe haber un cambio de contexto. La desventaja de esto es que el sistema puede hacer un cambio de contexto en un momento inadecuado, causando un fenómeno conocido como inversión de prioridades y otros problemas.

Multihilo cooperativo: depende del mismo hilo abandonar el control cuando llega a un punto de detención, lo cual puede traer problemas cuando el hilo espera la disponibilidad de un recurso.

El soporte de hardware para multihilo se encuentra disponible desde hace relativamente poco tiempo. Esta característica fue introducida por Intel en el Pentium 4, bajo el nombre de HyperThreading.

Usos más comunes

Page 19: HILOS Y SEMAFOROS

Los usos más comunes son en tecnologías SMPP y SMS para la telecomunicaciones aquí hay muchísimos procesos corriendo a la vez y todos requiriendo de un servicio.

Crear un Hilo

Crear un hilo en java es una tarea muy sencilla. Basta heredar de la

clase Thread y definir el método run(). Luego se instancia esta clase y se llama al

método start() para que arranque el hilo. Más o menos esto

public MiHilo extends Thread

{

   public void run()

   {

      // Aquí el código pesado que tarda mucho

   } 

};

...

MiHilo elHilo = new MiHilo();

elHilo.start();

System.out.println("Yo sigo a lo mio");

Listo. Hemos creado una clase MiHilo que hereda de Thread y con un

método run(). En el método run() pondremos el código que queremos que se

ejecute en un hilo separado. Luego instanciamos el hilo con un new MiHilo() y lo

arrancamos con elHilo.start(). El System.out que hay detrás se ejecutará

inmediatamente después del start(), haya terminado o no el código del hilo.

3.2 Comparación Programa Flujo Único y Flujo Múltiple

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.

Page 20: HILOS Y SEMAFOROS

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

public class Hola Mundo? {

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 hilo de ejecución (thread).

Debido a que la mayor parte de los entornos operativos no solían ofrecer un

soporte razonable para múltiples hilos de control, los lenguajes de programación

tradicionales, tales como C++, no incorporaron mecanismos para describir de

manera elegante situaciones de este tipo. La sincronización entre las múltiples

partes de un programa se llevaba a cabo mediante un bucle de suceso único.

Estos entornos son de tipo síncrono, gestionados por sucesos. Entornos tales

como el de Macintosh de Apple, Windows de Microsoft y X11/Motif fueron

diseñados en torno al modelo de bucle de suceso.

Programas de flujo múltiple

En la aplicación de saludo, no se ve el hilo de ejecución que corre el programa.

Sin embargo, Java posibilita la creación y control de hilos de ejecución

explícitamente. La utilización de hilos (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 hilos de ejecución,

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 hilos en Java. Habrá observado que dos applets se pueden ejecutar al

Page 21: HILOS Y SEMAFOROS

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 hiloss, sino

que el navegador es multihilo, multihilvanado o multithreaded.

Los navegadores utilizan diferentes hilos ejecutándose en paralelo para realizar

varias tareas, “aparentemente” concurrentemente. Por ejemplo, en muchas

páginas web, se puede desplazar la página e ir leyendo el texto antes de que

todas las imágenes estén presentes en la pantalla. En este caso, el navegador

está trayéndose las imágenes en un hilo de ejecución y soportando el

desplazamiento de la página en otro hilo diferente.

Las aplicaciones (y applets) multihilo 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 hilo de ejecución para

cada subtarea.

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

subtareas secuencialmente, un programa multihilo 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 el programa de saludo creando tres hilos de ejecución

individuales, que imprimen cada uno de ellos su propio mensaje de saludo, Multi

Hola.java:

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

// antes de imprimir sus nombres y retardos

class Test Th? extends Thread {

private String nombre;

private int retardo;

Page 22: HILOS Y SEMAFOROS

// Constructor para almacenar nuestro nombre

// y el retardo

public Test Th( 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( Interrupted Exception e ) {

;

}

// Ahora imprimimos el nombre

Page 23: HILOS Y SEMAFOROS

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

}

}

public class Multi Hola {

public static void main( String args[] ) {

Test Th t1,t2,t3;

// Creamos los threads

t1 = new Test Th( “Thread 1″,(int)(Math.random()*2000) );

t2 = new Test Th( “Thread 2″,(int)(Math.random()*2000) );

t3 = new Test Th( “Thread 3″,(int)(Math.random()*2000) );

// Arrancamos los threads

t1.start();

t2.start();

t3. start();

}

}

Page 24: HILOS Y SEMAFOROS

3.3 Creación y Control de Hilos

En Java, los hilos comparten el mismo espacio de memoria. Incluso comparten

gran parte del entorno de ejecución, de modo que la creación de nuevos hilos es

mucho más rápida que la creación de nuevos procesos. La ventaja que

proporcionan los hilos es la capacidad de tener más de un camino de ejecución en

un mismo programa. Así, con un único proceso, ejecutándose una JVM (Java

Virtual Machine), habrá siempre más de un hilo, cada uno con su propio camino de

ejecución.

En cuanto al proceso de creación de hilos, son dos los mecanismos que nos

permiten llevarlo a cabo en Java: implementando la interfaz Runnable, o

extendiendo la clase Thread, esto es, creando una subclase de esta clase.

Lo más habitual es crear hilos implementando la interfaz Runnable, dado que

lasinterfaces representan una forma de encapsulamiento del trabajo que una clase

debe realizar.

Así, se utilizan para el diseño de requisitos comunes a todas las clases que se

tiene previsto implementar. La interfaz define el trabajo, la funcionalidad que debe

cubrirse, mientras que la clase o clases que implementan la interfaz realizan dicho

trabajo (cumplen esa funcionalidad).

Todas las clases o grupos de clases que implementen una cierta interfaz deberán

seguir las mismas reglas de funcionamiento.

El otro mecanismo de creación de hilos, como ya hemos dicho, consistiría en la

creación previa de una subclase de la clase Thread, la cual podríamos instanciar

después.

Por ejemplo,

class Mi Thread extends Thread {

public void run() {

. . .

Page 25: HILOS Y SEMAFOROS

}

}

se corresponde con la declaración de un clase, Mi Thread, que extiende la clase

Thread, sobrecargando el método Thread.run heredado con su propia

implementación.

Es en el método run donde se implementa el código correspondiente a la acción

(la tarea) que el hilo debe desarrollar . El método run no es invocado directa o

explícitamente (a menos que no quieras que se ejecute dentro de su propio hilo).

En lugar de esto, los hilos se arrancan con el método start, se suspenden con el

método suspend, se reanudan con el método resume, y se detienen con el método

stop (el cual supone también la muerte del hilo y la correspondiente

excepción Thread Death ? ), como ya explicaremos en el apartado de Estado y

Control de Hilos. Un hilo suspendido puede reanudarse en la instrucción del

método run en la que fue suspendido.

En el caso de crear un hilo extendiendo la clase Thread, se pueden heredar los

métodos y variables de la clase padre. Si es así, una misma subclase solamente

puede extender o derivar una vez de la clase padre Thread. Esta limitación de

Java puede ser superada a través de la implementación de Runnable. Veamos el

siguiente ejemplo:

public class Mi Thread implements Runnable {

Thread t;

public void run() {

// Ejecución del thread una vez creado

}

}

Page 26: HILOS Y SEMAFOROS

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

pueda ejecutar el proceso como un hilo. Además, el método abstracto run que

está definido en la interfaz Runnable tiene que implementarse en la nueva clase

creada.

La diferencia entre ambos métodos de creación de hilos en Java radica en la

flexibilidad con que cuenta el programador, que es mayor en el caso de la

utilización de la interfaz Runnable.

Sobre la base del ejemplo anterior, se podría extender la clase Mi Thread a

continuación, si fuese necesario. La mayoría de las clases creadas que necesiten

ejecutarse como un hilo implementarán la interfaz Runnable, ya que así queda

cubierta la posibilidad de que sean extendidas por otras clases.

Por otro lado, es un error pensar que la interfaz Runnable está realizando alguna

tarea mientras un hilo de alguna clase que la implemente se está ejecutando. Es

una interfaz, y como tal, sólo contiene funciones abstractas (concretamente una

única, run), proporcionando tan solo una idea de diseño, de infraestructura, de la

clase Thread, pero ninguna funcionalidad. Su declaración en Java tiene el

siguiente aspecto:

package Java.lang;

public interfaz Runnable {

public abstract void run() ;

}

Comentados los aspectos más importantes de la interfaz Runnable, veamos ahora

la definición de la clase Thread, de la cual podemos deducir lo que realmente se

está haciendo:

public class Thread implements Runnable {

Page 27: HILOS Y SEMAFOROS

public void run() {

Java Threads (Hilos en Java)

6

if( tarea != null )

tarea.run() ;

}

…}

Se deduce, por tanto, que la propia clase Thread de Java también implementa la

interfaz Runnable. Observamos que en el método run de Thread se comprueba si

la clase con que se está trabajando en ese momento (tarea), que es la clase que

se pretende ejecutar como hilo, es o no nula. En caso de no ser nula, se invoca al

método run propio de dicha clase.

Control de un hilo

Arranque de un hilo

En el contexto de las aplicaciones, sabemos que es main la primera función que

se invoca tras arrancar, y por tanto, lógicamente, es el lugar más apropiado para

crear y arrancar otros hilos.

La línea de código:

t1 = new Test Th ? ( “Thread 1″,(int)(Math.random()*2000) );

Page 28: HILOS Y SEMAFOROS

siendo Test Th una subclase de la clase Thread (o una clase que implemente la

interfaz

Runnable) crea un nuevo hilo. Los dos argumentos pasados, sin mayor relevancia,

satisfarán

el prototipo del constructor de la clase y se utilizarán para la inicialización del

objeto.

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

Como ya se comentó anteriormente, es la función miembro start la que nos

permite hacerlo. En nuestro

ejemplo sería:

t1.start();

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

Manipulación de un hilo

Si todo fue bien en la creación del objeto Test Th (t1), éste debería contener un

hilo, una traza de ejecución válida, que controlaremos en el método run del objeto.

El cuerpo de esta función miembro viene a ser el cuerpo de un programa como ya

los conocemos. Digamos que es la rutina main a nivel de hilo. Todo lo que

queremos que haga el hilo debe estar dentro del método run. Cuando finalice run,

finalizará también el hilo que lo ejecutaba.

Suspensión de un Hilo

La función miembro suspend de la clase Thread permite tener un control sobre el

hilo de modo que podamos desactivarlo, detener su actividad durante un intervalo

de tiempo indeterminado, a diferencia del uso de la llamada al sistema sleep, que

simplemente lleva al hilo a un estado de “dormido”, y siempre durante un número

de milisegundos concreto.

Page 29: HILOS Y SEMAFOROS

Este método puede resultar útil si, construyendo un applet con un hilo de

animación, queremos permitir al usuario detener (que no finalizar) la animación,

hasta que éste decida reanudarla.

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

indefinidamente y para volver a activarlo de nuevo necesitamos realizar una

invocación a la función miembro resume.

Parada de un Hilo

Ya conocemos los métodos de control de hilos que nos permiten arrancarlos,

suspenderlos y reanudarlos. El último elemento de control que se necesita sobre

hilos es el método stop,utilizado para terminar la ejecución de un hilo de forma

permanente:

t1.stop();

Señalar que esta llamada no destruye el hilo, sino que detiene su ejecución, y ésta

no puede reanudarse con el método start. Cuando se desasignen las variables

que se usan en el hilo, el objeto hilo (creado con new) quedará marcado para

eliminarlo y el garbage collector (recolector de basura de Java) se encargará de

liberar la memoria que utilizaba.

Tiene sentido su utilidad, por ejemplo, en aplicaciones complejas que necesiten un

control sobre cada uno de los hilos que se lancen.

Por último, un método de control de hilos que nos permite comprobar si una

instancia está viva (el hilo se ha arrancado y aún no se ha detenido) o no (bien no

se arrancó; bien ya finalizó).

Estamos hablando de la función miembro isAlive.

t1.isAlive();

Page 30: HILOS Y SEMAFOROS

Devolverá true en caso de que el hilo 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. En otro caso, lógicamente, devolverá false.

3.3.1 atributos de un hilo

Los atributos de un thread

Cada thread cuenta con diferentes propiedades que lo hacen unico. Se adopta un

enfoque orientado a objetos con respecto a la representacion y asignacion de

propiedades. Bajo este paradigma cada thread cuenta con un objeto atributo

asociado a varios threads. Los objetos atributos son del tipo:

pthread_attr_t

Los atributos/propiedades de un thread varian de una implementacion a otra. Sin

embargo a manera general los atributos que denen a un thread son:

Estado de espera: permite que otros threads esperen por la terminacion de un

thread en especial.

Direccion de stack: apuntador al inicio del stack del thread

Tamano de la direccion: longitud del stack del thread.

Alcance (scope): dene quien controla la ejecucion del thread: el proceso o el

nucleo del sistema operativo.

Herencia: los parametros de calendarizacion son heredados o denidos

localmente.

Politica de calendarizacion: la politica que va a definir que proceso se va a ejecutar

y en que instante.

{

FIFO

Page 31: HILOS Y SEMAFOROS

{

Round-robin

{

Prioridad: un valor de prioridad alto corresponde a una mayor prioridad.

Es posible modicar varios de estos atributos a traves de diferentes llamadas de

sistema.

Lafuncion pthread_attr_init

asigna los atributos de default a los threads y la funcion

pthread_attr_destroy hace que el valor del objeto atributo sea invalido.

3.3.2 Inicialización y Creación de Hilos.

Antes de poder usar cualquier función de manejo de hilos, hay que preparar el

sistema para ejecutar estas operaciones, es decir, es necesario inicializar el

sistema de hilos, nada más sencillo.

Void g_thread_init (G Thread Functions *vtable );

En la mayoría de las ocasiones bastará con que el parámetro pasado al sistema

de hilos sea nulo, de esta forma G Lib ?  se encargará de buscar los parámetros

más apropiados.

Es importante no llamar a esta función más de una vez pues, si esto sucediese, la

ejecución de nuestro programa sería abortada. Para evitar esto la forma más

sencilla es hacer:

if (!g_thread_supported ())

g_thread_init (NULL);

Page 32: HILOS Y SEMAFOROS

Después, el sistema estará listo para realizar cualquier operación que requiera el

uso de hilos. Para empezar, se creará un hilo:

G Thread ?  *hilo:

hilo = g_thread_create (funcion_hilo, NULL, FALSE, NULL);

A partir de este punto, el procedimiento funcion_hilo se ejecutará en un hilo

diferente, el programa continuará su ejecución normal y, en paralelo a él, se

ejecutará esta función.

3.3.3 Manipulación de hilos

Si todo fue bien en la creación del objeto Test Th ?  (t1), éste debería contener un

hilo, una traza

de ejecución válida, que controlaremos en el método run del objeto.

El cuerpo de esta función miembro viene a ser el cuerpo de un programa como ya

los

conocemos. Digamos que es la rutina main a nivel de hilo. Todo lo que queremos

que haga el hilo debe estar dentro del método run. Cuando finalice run, finalizará

también el hilo que lo

ejecutaba.

3.3.4 Suspensión de un Hilo

La función miembro suspend de la clase Thread permite tener un control sobre el

hilo de modo que podamos desactivarlo, detener su actividad durante un intervalo

de tiempo indeterminado, a diferencia del uso de la llamada al sistema sleep, que

simplemente lleva al

hilo a un estado de “dormido”, y siempre durante un número de milisegundos

concreto.

Page 33: HILOS Y SEMAFOROS

Este método puede resultar útil si, construyendo un applet con un hilo de

animación,

queremos permitir al usuario detener (que no finalizar) la animación, hasta que

éste decida reanudarla.

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

indefinidamente y para volver a activarlo de nuevo necesitamos realizar una

invocación a la función miembro resume.

3.3.5 Parada De Hilos

Suspensión de un Hilo

 El último elemento de control que se necesita sobre los hilos de ejecución es el

método stop(). Se utiliza para terminar la ejecución de un hilo:

t1.stop();

Esta llamada no destruye el hilo, 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 hilo, 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 hilo está vivo o no; considerando vivo un

hilo que ha comenzado y no ha sido detenido.

t1.isAlive();

Este método devolverá true en caso de que el hilo 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.

En el ejemplo no hay problemas de realizar una parada incondicional, al estar

todos los hilos vivos. Pero si a un hilo de ejecución, que puede no estar vivo, se le

invoca su método stop(), se generará una excepción. En este caso, en los que el

estado del hilo no puede conocerse de antemano es donde se requiere el uso del

método isAlive().

Page 34: HILOS Y SEMAFOROS

3.4 Sincronización de hilos

Sincronización

El problema de la sincronización de hilos tiene lugar cuando varios hilos intentan

acceder al mismo recurso o dato. A la hora de acceder a datos comunes, los hilos

necesitan establecer cierto orden, por ejemplo en el caso del productor

consumidor. Para asegurarse de que hilos concurrentes no se estorban y operan

correctamente con datos (o recursos) compartidos, un sistema estable previene la

inanición y el punto muerto o interbloqueo. La inanición tiene lugar cuando uno o

más hilos están bloqueados al intentar conseguir acceso a un recurso compartido

de ocurrencias limitadas. El interbloqueo es la última fase de la inanición; ocurre

cuando uno o más hilos están esperando una condición que no puede ser

satisfecha. Esto ocurre muy frecuentemente cuando dos o más hilos están

esperando a que el otro u otros se desbloquee, respectivamente.

3.5 SEMAFOROS

Los semáforos se pueden considerar primitivas de bajo nivel en comparación con

otros modelos de paso de mensajes. Debido a su dificultad de utilización, resultan

poco apropiados para utilizar en los programas, por lo que se debería limitar a

módulos muy definidos.

Tipos de semáforos

Los semáforos operan con enteros no negativos. En función del rango de valores

posibles, hay dos tipos de semáforos:

Binarios: toman valores 0 y 1

Generales: toman los valores mayores de 0

Los lenguajes de programación normalmente trabajan con semáforos generales.

En caso de que no fuera así, se podrían implementar con semáforos binarios.

Page 35: HILOS Y SEMAFOROS

3.6 Barreras Barrier

BARRERA

Enunciado. Un proceso crea N hilos. El comportamiento de cada uno consiste en

la ejecución de un bucle con tres partes:

A

BARRERA

B

En cada iteración del bucle los hilos ejecutarán A sin realizar ninguna

sincronización; a continuación todos ellos esperan a que el resto llegue a la

BARRERA. Una vez que todos han llegado a la barrera (todos han ejecutado A),

continúan ejecutando B y el bucle se repite.

Salidas. Cada hilo deberá informar del número de iteración junto con el punto del

bucle que acaba de ejecutar.

Finalización. Ejecutar la iteración un cierto número de veces

Esquema del problema

Muchos problemas de cálculo se pueden resolver de acuerdo al siguiente

esquema:

Ejemplos:

–procesamiento de imágenes

–funciones definidas sobre mallas de puntos

–resolución de sistemas de ecuaciones

–problemas de optimización, etc.

Page 36: HILOS Y SEMAFOROS

La ejecución de “esperar al resto de procesos” es una sincronización, este tipo se

denomina “sincronización por barrera”.

Conclusión:

Los hilos a menudo son conocidos o llamados procesos ligeros. Un hilo, en

efecto, es muy similar a un proceso pero con la diferencia de que un hilo siempre

corre dentro del contexto de otro programa. Por el contrario, los procesos

mantienen su propio espacio de direcciones y entorno de operaciones. Los hilos

dependen de un programa padre en lo que se refiere a recursos de ejecución. La

siguiente figura muestra le relación entre hilos y procesos.

En Java, los hilos comparten el mismo espacio de memoria. Incluso comparten

gran parte del entorno de ejecución, de modo que la creación de nuevos hilos es

mucho más rápida que la creación de nuevos procesos. La ventaja que

proporcionan los hilos es la capacidad de tener más de un camino de ejecución en

un mismo programa. Así, con un único proceso, ejecutándose una JVM (Java

Virtual Machine), habrá siempre más de un hilo, cada uno con su propio camino de

ejecución.

Page 37: HILOS Y SEMAFOROS

Bibliografía:

1."Java como programar". Deitel y Deitel. 5º edición,

Pearson Education 2004.

2. "Programación en Java", Mora Rodríguez, Frank.Año: 2006.

3. http://www.lawebdelprogramador.com

4. "Aprenda Java como si estuviera en Primero".García de Jalón, Javier. Rodríguez, José Ignacio.y otros. Escuela Superior de Ingenieros Industriales. Universidad de Navarra. Marzo, 1999.

5. "Introducción a Java". Chojrin, Mauro. Año 2006.

BIBLIOGRAFIA

Elaborada por: Programación Avanzada y Métodos NuméricosIng. Laura Sandoval MontañoViridiana del Carmen De Luna BonillaVirgilio Green PérezEjemplo 1

http://www.hackerdude.com/courses/delphi/Cap011.1.html