unidad 2

11
Unidad 2 Concurrencia de hilos 2.1 Conceptos básicos 2.1.1 Definición de concurrencia. La concurrencia es la propiedad de los sistemas que permiten que múltiples procesos sean ejecutados al mismo tiempo, y que potencialmente puedan interactuar entre sí, los procesos concurrentes pueden ser ejecutados realmente de forma simultánea, sólo cuando cada uno es ejecutado en diferentes procesadores. En cambio, la concurrencia es simulada si sólo existe un procesador encargado de ejecutar los procesos concurrentes, simulando la concurrencia, ocupándose de forma alternada en uno y otro proceso a pequeñísimos intervalos de tiempo. De esta manera simula que se están ejecutando a la vez. 2.1.2 Definición de hilos. En sistemas operativos, un hilo de ejecución, hebra o subproceso es la unidad de procesamiento más pequeña que puede ser planificada por un sistema operativo. La creación de un nuevo hilo 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 simplemente una tarea que puede ser ejecutada al mismo tiempo 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. 2.2 Implementación de hilos.

Upload: lennyta-hernandez

Post on 29-Jan-2016

212 views

Category:

Documents


0 download

DESCRIPTION

trabajo

TRANSCRIPT

Page 1: Unidad 2

Unidad 2 Concurrencia de hilos

2.1 Conceptos básicos

2.1.1 Definición de concurrencia.

La concurrencia es la propiedad de los sistemas que permiten que múltiples procesos sean ejecutados al mismo tiempo, y que potencialmente puedan interactuar entre sí, los procesos concurrentes pueden ser ejecutados realmente de forma simultánea, sólo cuando cada uno es ejecutado en diferentes procesadores. En cambio, la concurrencia es simulada si sólo existe un procesador encargado de ejecutar los procesos concurrentes, simulando la concurrencia, ocupándose de forma alternada en uno y otro proceso a pequeñísimos intervalos de tiempo. De esta manera simula que se están ejecutando a la vez.

2.1.2 Definición de hilos.

En sistemas operativos, un hilo de ejecución, hebra o subproceso es la unidad de procesamiento más pequeña que puede ser planificada por un sistema operativo.

La creación de un nuevo hilo 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 simplemente una tarea que puede ser ejecutada al mismo tiempo 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.

2.2 Implementación de hilos.

2.2.1 Estados de un hilo.

Un hilo tiene un ciclo de vida que va desde su creación hasta su terminación. Durante su ciclo de vida cada uno de los hilos o tareas de una aplicación puede estar en diferentes estados, algunos de los cuales se indican a continuación:

• Nacido: Cuando se acaba de crear un hilo, se dice que está nacido, y continúa en ese estado hasta que se invoca el método start() del hilo. La siguiente sentencia crea un nuevo thread pero no lo arranca, por lo tanto deja el thread en el estado de nacido.Thread miHilo = new MiClaseThread();Cuando un thread está en este estado, es sólo un objeto Thread vacío o nulo. No se han asignado recursos del sistema todavía para el thread. Así, cuando un thread está en este estado, lo único que se puede hacer es arrancarlo con start().

Page 2: Unidad 2

• Listo: Cuando se invoca el método start() del hilo, se dice que está en estado listo. El método se arranca con la siguiente instrucción, para el caso del hilo miHilo: miHilo.start();

• Ejecutable: cuando el método start() se ejecuta, crea los recursos del sistema necesarios para ejecutar el thread, programa el thread para Programación de Hilos: Ciclo de vida de un hilo 12 – 5 ejecutarse, y llama al método run() del thread que se ejecuta en forma secuencial. En este punto el thread está en el estado ejecutable. Se denomina así puesto que todavía no ha empezado a ejecutarse.

• En ejecución: Un hilo en estado de listo de la más alta prioridad, pasa al estado de ejecución, cuando se le asignan los recursos de un procesador, o sea cuando inicia su ejecución. Aquí el thread está en ejecución.Cada hilo tiene su prioridad, hilos con alta prioridad se ejecutan preferencialmente sobre los hilos de baja prioridad.

• No ejecutable: Un hilo continúa la ejecución de su método run(), hasta que pasa al estado de no ejecutable originado cuando ocurre alguno de los siguientes cuatro eventos:Se invoca a su método sleep().Se invoca a su método suspend().El thread utiliza su método wait() para esperar una condición variable.El thread está bloqueado durante una solicitud de entrada/salida. Por ejemplo, en el siguiente fragmento de codigo se pone a dormirmiHilo durante 10 segundos (10.000 milisegundos):Thread miHilo = new MiClaseThread();miHilo.start();try {

miHilo.sleep(10000); } catch (InterruptedException e){}

Durante los 10 segundos que miHilo está dormido, incluso si el proceso se vuelve disponible, miHilo no se ejecuta. Después de 10 segundos, miHilo se convierte en "Ejecutable" de nuevo y, si el procesador está disponible se ejecuta. Para cada entrada en el estado "No Ejecutable", existe una ruta de escape distinto y específico que devuelve el thread al estado "Ejecutable". Por ejemplo, si un thread ha sido puesto a dormir durante un cierto número de milisegundos deben pasar esos milisegundos antes de volverse "Ejecutable" de nuevo. La siguiente es una secuencia de las acciones a realizar para cada entrada en el estado "No Ejecutable":Programación de Hilos: Ciclo de vida de un hilo 12 - 6Si se ha puesto a dormir un hilo, deben pasar el número de milisegundos especificados en sleep().Si se ha suspendido un hilo, se debe llamar a su método resume().Si un hilo está esperando una condición variable, siempre que el objeto propietario de la variable renuncie mediante notify() o notifyAll().Si un hilo está bloqueado durante la I/O, cuando se complete la I/O.

• Muerto: Un hilo pasa al estado de muerto cuando se termina su método run(), o cuando se ha invocado su método stop(). En algún momento el sistema dispondrá entonces del hilo muerto. Un hilo puede morir de dos formas:Muerte natural: se produce cuando su método run() sale normalmente. Por ejemplo, el bucle while en este método es un bucle que itera 100 veces y luego sale. Por tanto el hilo

Page 3: Unidad 2

morirá naturalmente cuando se llegue al final de la iteración, es decir se termina su método run().public void run() {int i = 0;

while (i < 100) { i++; System.out.println("i = " + i);}

}Por muerte provocada: en cualquier momento llamando a su método stop(). El siguiente código crea y arranca miHilo luego lo pone a dormir durante 10 segundos. Cuando el thread actual se despierta, se lo mata con miHilo.stop(). El método stop() lanza un objeto ThreadDeath hacia al hilo a eliminar. El thread moririá cuando reciba realmente la excepción ThreadDeath.Thread miHilo = new MiClaseThread();miHilo.start();try {

Thread.currentThread().sleep(10000);} catch (InterruptedException e){}miHilo.stop();El método stop() provoca una terminación súbita del método run() del hilo. Si el método run() estuviera realizando cálculos sensibles, stop() podría dejar el programa en un estado inconsistente. Normalmente, no se debería Programación de Hilos: Ciclo de vida de un hilo 12 – 7 llamar al método stop() pero si se debería proporcionar una terminación educada como la selección de una bandera que indique que el método run() debería salir. El método stop() se encuentra depreciado en la versión JDK1.2.1.

• Bloqueado: un hilo se encuentra en el estado bloqueado cuando el hilo realiza una solicitud de entrada/salida. Cuando termina la entrada/salida que estaba esperando, un hilo bloqueado queda en el estado listo.

2.2.2 Prioridades de un hilo.

Aunque un programa utilice varios threads y aparentemente estos se ejecuten simultáneamente, el sistema ejecuta una sola instrucción cada vez (esto es particularmente cierto en sistemas con una sola CPU), aunque realizado a velocidad suficiente para proporcionar la ilusión de simultaneidad. El mecanismo por el cual un sistema controla la ejecución concurrente de procesos se llama planificación (scheduling). Java soporta un mecanismo simple denominado planificación por prioridad fija (fixed priority scheduling). Esto significa que la planificación de los threads se realiza en base a la prioridad relativa de un thread frente a las prioridades de otros.

La prioridad de un thread es un valor entero (cuanto mayor es el número, mayor es la prioridad), que puede asignarse con el método setPriority. Por defecto la prioridad de un thread es igual a la del thread que lo creo. Cuando hay varios threads en condiciones de ser ejecutados (estado runnable), la máquina virtual elige el thread que tiene una prioridad mas alta, que se ejecutaran hasta que:

Page 4: Unidad 2

Un thread con una prioridad más alta está en condiciones de ser ejecutado (runnable), o

El thread termina (termina su metodo run), o Se detiene voluntariamente o  Alguna condición hace que el thread no sea ejecutable (runnable), como una

operación de entrada/salida o, si el sistema operativo tiene planificación por división de tiempos (time slicing), cuando expira el tiempo asignado.

Si dos o más threads están listos para ejecutarse y tienen la misma prioridad, la máquina virtual va cediendo control de forma cíclica (round-robin).

El hecho de que un thread con una prioridad más alta interrumpa a otro se denomina se denomina 'planificación con derecho preferente' (preemptive scheduling). 

Cuando un thread entra en ejecución y no cede voluntariamente el control para que puedan ejecutarse otros threads, se dice que es un thread egoísta (selfish thread). Algunos Sistemas Operativos, como Windows, combaten estas actitudes con una estrategia de planificación por división de tiempos (time-slicing), que opera con threads de igual prioridad que compiten por la CPU. En estas condiciones el Sistema Operativo asigna tiempos a cada thread y va cediendo el control consecutivamente a todos los que compiten por el control de la CPU, impidiendo que uno de ellos se apropie del sistema durante un intervalo de tiempo prolongado.

Este mecanismo lo proporciona el sistema operativo, no Java. 

2.2.3 Sincronización de hilos.

Cuando se están utilizando hilos múltiples,algunas veces es necesario coordinar las actividades de dos o más. El proceso por el cual se logra esto se llama sincronización. La razón más común para la sincronización es cuando dos o mas hilos necesitan acceso a un recurso compartido que  sólo puede ser utilizado por un hilo a la vez. Otra razón para la sincronización es cuando un hilo está esperando un evento causado por otro hilo. En este caso, debe de haber algún medio por el cual el primer hilo se mantenga en estado suspendido hasta que el evento ocurra.     La sincronización esta soportada por la palabra clave synchronized y por unos cuantos métodos bien definidos que tienen todos los objetos.

USO DE MÉTODOS SYNCHRONIZED     Cuando se llama al método Synchronized , el hilo que llama ingresa al monitor de objeto, que entonces bloquea el objeto. Mientras esta bloqueado ningún otro hilo puede ingresar al método, ni ingresar algún otro método sincronizado definido por el objeto. Cuando el hilo retorna del método, el monitor desbloquea el objeto permitiendo que sea usado por el próximo hilo.

Los puntos clave de un método sincronizado son:

Un método sincronizado se crea precediendo su declaración con synchronized.

Page 5: Unidad 2

Para cualquier objeto dado,una vez un método sincronizado ha sido llamado se bloquea el objeto, y los métodos no sincronizados dentro del mismo objeto pueden ser utilizados por otros hilos en ejecución.

Otros hilos que traten de llamar un objeto sincronizado en uso ingresaría en un estado de espera, hasta que el objeto se desbloquea.

Cuando un hilo sale del método sincronizado, el objeto se desbloquea.

DECLARACIÓN SYNCHRONIZED    La creación del método Synchronized dentro de las clases creadas por nosotros mismos es fácil y eficiente sin embargo no trabaja en todos los casos. Por ejemplo, podemos querer sincronizar el acceso a algún método que no este modificado por Synchronized  ; esto ocurre cuando queremos utilizar una clase que no fue creada por nosotros sino por un tercero, y no tenemos acceso al código fuente.     La solución para este problema es poner llamadas a los métodos definidos por esa clase dentro de un bloque Synchronized. La forma general de un bloque  Synchronized es:

synchronized  (objeto){                                   //declaraciones para ser sincronizadas

                                                            }

     Aquí objeto hace referencia al objeto que va a ser sincronizado. Un objeto sincronizado asegura que una llamada a un método, ocurra solo después de que el hilo que llama ha ingresado al monitor del objeto.

EJEMPLO:/**

uso de synchronized en el control de accesos */class SumArray {          private int sum;

          synchronized int SumArray(int nums[]){           sum=0; //inicializa sum

           for (int i=0;i<nums.length;i++){               sum= nums[i];                System.out.println ("Ejecucion total para" + Thread.currentThread().getName() + " es " + sum);

          try{               Thread.sleep(10); //permite el suicheo de tareas          }         catch (InterruptedException exc){                  System.out.println ("Hilo principal interrumpido ");           }        }     return sum;  }    

Page 6: Unidad 2

}class MyThread implements Runnable{                 Thread thrd;                 static  SumArray sa=new SumArray();                 int a[];                 int answer;

                / /contruye un nuevo hilo              MyThread(String name,int nums[]){                       thrd= new Thread (this,name);                       thrd.start(); //arranca el hilo                        a= nums;              }

             //inicia la ejecucion del nuevo hilo            public void run(){                       int sum;                       System.out.println ("Suma para " + thrd.getName() + " es " + answer);                       System.out.println (thrd.getName() + "terminando");          }

}class Sync{          public static void main (String[] args) {          int a[]={1,2,3,4,5};

          MyThread mt1= new MyThread ("Hijo #1",a);          MyThread mt2= new MyThread ("Hijo #2",a);         }}

El programa anterior crea 3 clases. La primera es SumArray que contiene el método SumArray (), que suma un arreglo entero. La segunda clase es MyThread, que utiliza un objeto de tipo SumArray para obtener la suma de un arreglo entero. Finalmente, la clase Sync crea dos hilos, y permite que ellos calculen la suma de un arreglo entero.

    Dentro de sumArray (), se llama sleep() para permitir que ocurra un cambio de tarea, pero de hecho no es posible. Porque sumArray () está sincronizada. Solamente un hilo a la vez puede utilizarla. De modo que cuando el segundo hilo hijo comience su ejecución, no ingresa sumArray() hasta después que el primer hilo hijo esté terminado. Esto asegura que se produzca el resultado correcto. 

COMUNICACIÓN DE HILOS UTILIZANDO NOTIFY (), WAIT() Y NOTIFY ALL ()

     Los métodos wait (), notify () y notify all () son parte de todos los objetos porque están implementados por la clase Object. Estos métodos sólo pueden ser llamados desde

Page 7: Unidad 2

el interior de un método Synchronized. A continuación se presenta como se utilizan. Cuando un hilo está temporalmente bloqueado para ejecución, llama a wait (). Esto hace que el hilo vaya a dormir y que el monitor para ese objeto se libere, permitiendo que otro hilo utilice el objeto. Luego en otro punto, el hilo dormido despierta cuando algún otro hilo ingresa al monitor, y llama a notify () o a notify All (). Un llamado a notify () reanuda el hilo. Una llamada a notify All () reanuda todos los hilos de más alta prioridad gana el acceso al objeto.          A continuación, se muestran varias formas de wait () definidas por Object:

final void wait() throws InterruptedException final void wait ( long milísegundos ) throws InterruptedException final void wait ( long milisegundos, int nanosegundos ) throwsInterrupted-Exception   

   La primera forma espera hasta que sea notificada. La segunda forma espera hasta que sea notificada o hasta que expire el período de milísegundos. La tercera forma le permite a usted especificar el período a aguardar en términos de nanosegundos.    A continuación, se muestran las formas generales de notify () y notify All ():

final void notify () final void notifyAll ()

EJEMPLO:    Para comprender la necesidad y aplicación de wait() y notify (), crearemos un programa que simule el tic-tac de un reloj, mostrando las palabras "tic" y "tac" en la pantalla. Para realizar esto, crearemos una clase llamada TickTock que contiene dos métodos: tick() y tock().    Paja ejecutar el reloj se crean dos hilos, uno que llama a tick () y otro, a tock (). El objetivo es hacer que los dos hilos se ejecute de modo que el resultado del programa presentes un "tic-tac" constante.

/* uso de wait () y notify () para crear un reloj de tarjeta. */class TickTock {      synchronized void tick ( boolean running ) {          if ( !running ) { //para el reloj                notify ( ); // notifica una espera para el hilo          return:         }            System.out.println ( "Tic ");          notify ( ); // permite la ejecución del tock ()          try {                wait ( ); // espera que tock ( ) se complete          }          catch ( InterruptedException exc ) {                System.out.println ( "del Hilo interrumpido  ");          }      }     synchronized void tock ( boolean running ) {               if (!running) { //para el reloj

Page 8: Unidad 2

                     notify  ( ); // notifica una espera para el hilo               return;               }                System.out.println ( "Toc");               notify ( );               try {                      wait ( ); // espera que tick ( ) se complete               }               catch ( InterruotedException exc ) {                      System.out.println ( "Hilo interrumpido");               }     }}

class MyThread implements Runnable {         Thread thrd;         TickTock ttOb;

        // construye un nuevo hilo        MyThread ( String name.TickTock tt ) {                thrd= new Thread ( this.name );                ttOb= tt;                thrd.start ( ); // arranca el hilo       }      //inicia la ejecución del nuevo hilo      public void main ( ) { // El hilo comienza a ejecutarse aquí              if ( thrd.getName ( ).compareTo ( "Tick") == 0 ) {                   for ( int i=0; i<5; i++ ) ttOb.tick ( true );                            ttOb.tock ( false );             }             else {                  for ( int i=0; i<5; i++ ) ttOb.tock ( true );                          ttOb.tock ( false );             }      }}

class ThreadCom {        public static void main ( String args [ ]) {                  TickTock t1= new TickTock ( );                  MyThread mt1= new MyThread ( "Tick",tt );                  MyThread mt2= new MyThread ( "Tock",tt );             try {                 mt1.thrd.join ( );                 mt2.thrd.join ( );           } catch ( InterruptedException exc ) {                     System.out.println ( "Procedimiento interrumpido ");          }     }}

Page 9: Unidad 2

2.2.4 Aplicaciones.